1
0
mirror of https://github.com/adambard/learnxinyminutes-docs.git synced 2025-08-06 23:06:49 +02:00

Merge pull request #275 from jdonaldson/master

More updates to the haxe guide
This commit is contained in:
Adam Bard
2013-08-27 20:53:19 -07:00

View File

@@ -16,40 +16,62 @@ references.
Welcome to Learn Haxe 3 in 15 minutes. http://www.haxe.org 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 This is an executable tutorial. You can compile and run it using the haxe
compiler, while in the same directory as LearnHaxe.hx: 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 This is your first actual haxe code coming up, it's declaring an empty
javadoc-style documentation for haxedoc. They will be used if they precede package. A package isn't necessary, but it's useful if you want to create a
a class, class function, or class variable. namespace for your code (e.g. org.module.ClassName).
*/
/*
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:
*/ */
package; // empty package, no namespace. 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 If you import code from other files, it must be declared before the rest of
// the code. the code. Haxe provides a lot of common default classes to get you started:
*/
import haxe.ds.ArraySort; import haxe.ds.ArraySort;
// you can import many classes/modules at once with "*" // you can import many classes/modules at once with "*"
import haxe.ds.*; 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; 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{ class LearnHaxe3{
/* /*
If you want certain code to run automatically, you need to put it in If you want certain code to run automatically, you need to put it in
@@ -58,6 +80,7 @@ class LearnHaxe3{
arguments above. arguments above.
*/ */
static function main(){ static function main(){
/* /*
Trace is the default method of printing haxe expressions to the Trace is the default method of printing haxe expressions to the
screen. Different targets will have different methods of 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 Finally, It's possible to prevent traces from showing by using the
"--no-traces" argument on the compiler. "--no-traces" argument on the compiler.
*/ */
trace("Hello World, with trace()!"); trace("Hello World, with trace()!");
/* /*
@@ -76,16 +97,11 @@ class LearnHaxe3{
a representation of the expression as best it can. You can also a representation of the expression as best it can. You can also
concatenate strings with the "+" operator: concatenate strings with the "+" operator:
*/ */
trace( trace( " Integer: " + 10 + " Float: " + 3.14 + " Boolean: " + true);
" Integer: " + 10 +
" Float: " + 3.14 +
" Boolean: " + true
);
/* /*
Remember what I said about expressions needing semicolons? You In Haxe, it's required to separate expressions in the same block with
can put more than one expression on a line if you want. semicolons. But, you can put two expressions on one line:
*/ */
trace('two expressions..'); trace('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 You can save values and references to data structures using the
"var" keyword: "var" keyword:
*/ */
var an_integer:Int = 1; var an_integer:Int = 1;
trace(an_integer + " is the value for an_integer"); 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 the haxe compiler is inferring that the type of another_integer
should be "Int". should be "Int".
*/ */
var another_integer = 2; var another_integer = 2;
trace(another_integer + " is the value for another_integer"); 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 var a_string = "some" + 'string'; // strings can have double or single quotes
trace(a_string + " is the value for a_string"); 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 x = 1;
var an_interpolated_string = 'the value of x is $x'; 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 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); var a_sub_string = a_string.substr(0,4);
trace(a_sub_string + " is the value for a_sub_string"); 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 Arrays are zero-indexed, dynamic, and mutable. Missing values are
defined as null. defined as null.
@@ -191,7 +217,7 @@ class LearnHaxe3{
trace(m3 + " is the value for m3"); 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 List, Stack, and BalancedTree
*/ */
@@ -199,7 +225,6 @@ class LearnHaxe3{
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// Operators // Operators
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
trace("***OPERATORS***"); trace("***OPERATORS***");
// basic arithmetic // 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");
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 ~ Unary bitwise complement
<< Signed left shift << Signed left shift
@@ -252,6 +277,27 @@ class LearnHaxe3{
trace("also not printed."); 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"); trace("Looping and Iteration");
// while loop // while loop
@@ -310,13 +356,14 @@ class LearnHaxe3{
generalized algebraic data types in enums (more on enums later). generalized algebraic data types in enums (more on enums later).
Here's some basic value examples for now: Here's some basic value examples for now:
*/ */
var my_dog_name = 'fido'; var my_dog_name = "fido";
var favorite_thing = ''; var favorite_thing = "";
switch(my_dog_name){ switch(my_dog_name){
case "fido" : favorite_thing = 'bone'; case "fido" : favorite_thing = "bone";
case "rex" : favorite_thing = 'shoe'; case "rex" : favorite_thing = "shoe";
case "spot" : favorite_thing = 'tennis ball'; case "spot" : favorite_thing = "tennis ball";
case _ : favorite_thing = 'some unknown treat'; default : favorite_thing = "some unknown treat";
// case _ : "some unknown treat"; // same as default
} }
// The "_" case above is a "wildcard" value // The "_" case above is a "wildcard" value
// that will match anything. // that will match anything.
@@ -345,10 +392,10 @@ class LearnHaxe3{
trace("K equals ", k); // outputs 10 trace("K equals ", k); // outputs 10
var other_favorite_thing = switch(my_dog_name) { var other_favorite_thing = switch(my_dog_name) {
case "fido" : 'teddy'; case "fido" : "teddy";
case "rex" : 'stick'; case "rex" : "stick";
case "spot" : 'football'; case "spot" : "football";
case _ : 'some unknown treat'; default : "some unknown treat";
} }
trace("My dog's name is" + my_dog_name trace("My dog's name is" + my_dog_name
@@ -358,6 +405,7 @@ class LearnHaxe3{
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// Converting Value Types // Converting Value Types
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
trace("***CONVERTING VALUE TYPES***");
// You can convert strings to ints fairly easily. // You can convert strings to ints fairly easily.
@@ -372,33 +420,93 @@ class LearnHaxe3{
true + ""; // returns "true"; true + ""; // returns "true";
// See documentation for parsing in Std for more details. // 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 // Basic Object Oriented Programming
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
trace("***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. Create an instance of FooClass. The classes for this are at the
var instance = new FooClass(3); end of the file.
*/
var foo_instance = new FooClass(3);
// read the public variable normally // 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 // 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 // but not write it
// instance.public_write = 4; // this will throw an error if uncommented: // foo_instance.public_write = 4; // this will throw an error if uncommented:
// trace(instance.public_write); // as will this. // trace(foo_instance.public_write); // as will this.
trace(instance + " is the value for instance"); // calls the toString method trace(foo_instance + " is the value for foo_instance"); // calls the toString method
trace(instance.toString() + " is the value for instance.toString()"); // same thing 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, The foo_instance has the "FooClass" type, while acceptBarInstance
// it is accepted. has the BarClass type. However, since FooClass extends BarClass, it
BaseFooClass.acceptBaseFoo(instance); 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 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_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_read (default,null): Int; // use this style to only enable public read
public var public_write (null, default): Int; // or public write public var public_write (null, default): Int; // or public write
@@ -418,7 +526,7 @@ class FooClass extends BaseFooClass implements BaseFooInterface{
// a public constructor // a public constructor
public function new(arg:Int){ 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.public_any= 0;
this._private = arg; this._private = arg;
@@ -442,7 +550,7 @@ class FooClass extends BaseFooClass implements BaseFooInterface{
} }
// this class needs to have this function defined, since it implements // this class needs to have this function defined, since it implements
// the BaseFooInterface interface. // the BarInterface interface.
public function baseFunction(x: Int) : String{ public function baseFunction(x: Int) : String{
// convert the int to string automatically // convert the int to string automatically
return x + " was passed into baseFunction!"; return x + " was passed into baseFunction!";
@@ -452,21 +560,217 @@ class FooClass extends BaseFooClass implements BaseFooInterface{
/* /*
A simple class to extend A simple class to extend
*/ */
class BaseFooClass { class BarClass {
var base_variable:Int; var base_variable:Int;
public function new(){ public function new(){
base_variable = 4; base_variable = 4;
} }
public static function acceptBaseFoo(b:BaseFooClass){ public static function acceptBarInstance(b:BarClass){
} }
} }
/* /*
A simple interface to implement A simple interface to implement
*/ */
interface BaseFooInterface{ interface BarInterface{
public function baseFunction(x:Int):String; 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).