Haxe

Личный сайт Go-разработчика из Казани

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.