diff options
author | Adam Bard <github@adambard.com> | 2013-08-27 20:53:19 -0700 |
---|---|---|
committer | Adam Bard <github@adambard.com> | 2013-08-27 20:53:19 -0700 |
commit | 041f1bb6e5415dbb65ee7038c281a29f8b70a160 (patch) | |
tree | 272cf39b5978d35c447c857c811d299b7652c591 | |
parent | 9895f1ade9b792ebeb32834415570d53804426ad (diff) | |
parent | 0f82b183493a1f6142a6e7335d37891aae67c3dc (diff) |
Merge pull request #275 from jdonaldson/master
More updates to the haxe guide
-rw-r--r-- | haxe.html.markdown | 426 |
1 files changed, 365 insertions, 61 deletions
diff --git a/haxe.html.markdown b/haxe.html.markdown index 90b2e250..f4694fcb 100644 --- a/haxe.html.markdown +++ b/haxe.html.markdown @@ -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 -// Let's start with comments... this is a single line comment + 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. -/* - 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. */ +// Double slashes like this will give a single-line comment + + /* - 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. - -// if you import code from other files, it must be declared before the rest of -// the code. +/* + 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. 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 @@ -149,6 +169,12 @@ class LearnHaxe3{ 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(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 - trace(instance + " is the value for instance"); // calls the toString method - trace(instance.toString() + " is the value for instance.toString()"); // same thing + /* + 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(); - // instance has the "FooClass" type, while acceptBaseFoo has the - // BaseFooClass type. However, since FooClass extends BaseFooClass, - // it is accepted. - BaseFooClass.acceptBaseFoo(instance); } } @@ -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). + + |