Syntax

In Haxe, all statements are expressions, which means they can be nested. For example, foo(if (x == 3) 5 else 8) is valid Haxe. As this example shows, every expression returns a value of a given type.

Constants

Here are some examples of constants in Haxe:

    0; // Int
    -134; // Int
    0xFF00; // Int

    123.0; // Float
    .14179; // Float
    13e50; // Float
    -1e-99; // Float

    "hello"; // String
    "hello \"world\" !"; // String
    'hello "world" !'; // String

    true; // Bool
    false; // Bool

    null; // Unknown<0>

    ~/[a-z]+/i; // EReg : regular expression

Notice that null has a special value, since any value can be null. Notice also that it is not Dynamic. This will be explained in detail when we introduce type inference.

Operations

The following operations can be used, in order of priority:

  • Unary operations (see next section below).
  • e1 % e2 : calculates e1 modulo e2, Return Int if they were both Int, otherwise return Float.
  • e1 / e2 : divide two numbers. Always return Float.
  • e1 * e2 : multiply two numbers. Same return type as modulo.
  • e1 - e2 : Subtract Ints or Floats. Same return type as modulo.
  • e1 + e2 : perform addition. Same return type as modulo.
  • << >> >>> : perform bitwise shifts between two Int expressions. Returns Int.
  • | & ^ : perform bitwise operations between two Int expressions. Returns Int.
  • == != > < >= <= : perform normal or physical comparisons between two expressions sharing a common type. Returns Bool.
  • e1...e2 : Build an IntIter from e1 to e2 - see Iterators for more information.
  • e1 && e2 : If e1 is false then false else evaluate e2 . Both e1 and e2 must be Bool.
  • e1 || e2 : If e1 is true then true else evaluate e2 . Both e1 and e2 must be Bool.
  • and += -= *= /= %= &= |= ^= <<= >>= >>>= : assign after performing the corresponding operation
  • v = e : assign a value to an expression, returns e

Note: Associativity of operators can be found here here

Unary operations

The following unary operations are available :

  • ! : boolean not. Inverse the expression Bool value.
  • - : negative number, change the sign of the Int or Float value.
  • ++ and -- can be used before or after a variable. When used before, they first increment the corresponding variable and then return the incremented value. When used after, they increment the variable but return the value it had before incrementation. Can only be used with Int or Float variables.
  • ~ : ones-complement of an Int.

Note: ~ is usually used for 32-bit integers, so it is incompatible with Neko's 31-bits integers.

Parentheses

Surrounding an expression with parenthesis will give that expression higher priority when it is to be evaluated. The type of ( e ) is the same as e, and they both evaluate to the same value.

Blocks

Blocks are a group of several expressions. The syntax of a block is:

    {
        e1;
        e2;
        // ...
        eX;
    }

A block evaluates to the type and value of the last expression of the block. For example :

    { f(); x = 124; true; }

This block is of type Bool and will evaluate to true.

The empty block { } evaluates to Void.

Variable Naming

Variable names are case sensitive in Haxe. A valid variable name starts with a letter or underscore, followed by any number of letters, numbers, or underscores. As a regular expression, it would be expressed thus: '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'

Local Variables

Local variables can be declared in blocks using var, as the following sample shows:

    {
        var x;
        var y = 3;
        var z : String;
        var w : String = "";
        var a, b : Bool, c : Int = 0;
    }

A variable can be declared with an optional type and an optional initial value. If no value is given then the variable is null by default. If no type is given, then the variable type is Unknown but will still be strictly typed. This will be explained in details when introducing type inference.

Several local variables can be declared in the same var expression.

Local variables are only defined until the block they're declared in is closed. They can not be accessed outside the block in which they are declared (i.e. Haxe has lexical scoping).

Identifiers

When a variable identifier is resolved in the following order of precedence:

  • local variables, last declared having priority
  • class members (current class and inherited fields)
  • current class static fields
  • enum constructors that have been either declared in this file or imported
    enum Axis {
        x;
        y;
        z;
    }

    class C {
        static var x : Int;
        var x : Int;

        function new() {
            {
               // x at this point means member variable this.x
                var x : String;
                // x at this point means the local variable
            }
        }

        function f(x : String) {
            // x at this point means the function parameter
        }

        static function f() {
            // x at this point means the class static variable
        }
    }

    class D {
        function new() {
            // x means the x Axis
        }
    }

Type identifiers are resolved according to the imported packages, as we will explain later.

Field access

Member access is done using the traditional dot notation :

    o.field

Calls

You can call functions using parentheses and commas in order to delimit arguments. You can call methods by using dot access on objects :

    f(1,2,3);
    object.method(1,2,3);

New

The new keyword is used to create a class instance. It needs a class name and can take parameters :

    a = new Array();
    s = new String("hello");

Arrays

You can create arrays from a list of values like this:

    var a : Array<Int> = [1,2,3,4];

This is called an array literal.

Notice that Array takes one type parameter - the type of the items stored in the Array. All items in the array have to be of this type - this ensures type safety. (If you want a heterogeneous array, try Array<Dynamic> — we'll learn about that in a bit.)

Multidimensional arrays can be created by nesting these type parameters in the declaration:

    var a : Array<Array<Int>> = [
        [1, 2, 3],
        [4, 5],
        [6, 7, 8, 9], // Terminal comma is permitted but ignored.
    ];

You can read and write into an Array by using the following traditional bracket access :

    first = a[0];
    a[1] = value;

The array index must be of type Int.

Also see Array Comprehension for more advanced array features.

If

Here are some examples of if expressions :

    if (life == 0) destroy();
    if (flag) 1 else 2;

Here's the generic syntax of if expressions :

    if( expr-cond ) expr-1 [else expr-2]

First expr-cond is evaluated. It must be of type Bool. If it evaluates to true then expr-1 is evaluated, otherwise, if there is an expr-2 then it is evaluated instead.

If there is no else, and the if expression is false, then the entire expression has type Void. If there is an else, then expr-1 and expr-2 must be of the same type and this will be the type of the if expression :

    var x : Void = if( flag ) destroy();
    var y : Int = if( flag ) 1 else 2;

In Haxe, if is similar to the ternary C a?b:c syntax. You can use ternary syntax if you want.

    var x  = ( x < upperBound )? x: upperBound;

As an exception, if an if block is not supposed to return any value (like in the middle of a Block) then both expr-1 and expr-2 can have different types and the if block type will be Void.

While

While are standard loops that use a precondition or postcondition :

    while( expr-cond ) expr-loop;
    do expr-loop while( expr-cond );

For example :

    var i = 0;
    while( i < 10 ) {
        // ...
        i++;
    }

Or using do...while :

    var i = 0;
    do {
        // ...
        i++;
    } while( i < 10 );

Like with if, the expr-cond in a while-loop type must be of type Bool.

Another useful example will produce a loop to count from 10 to 1:

    var i = 10;
    while( i > 0 ) {
    .......
    i--;
    }

For

For loops are different from traditional C for loops, in that they can only be used with Iterators.

Here's an example of a for loop:

    for( i in 0...a.length ) {
        foo(a[i]);
    }

If the iterator was created with ... operator, with its second term smaller than or equal to the first, then the loop will not execute.
If the ... operator is used as above (inline, inside the for), then a smaller second term will give a compiler error, explaining the problem.

Return

In order to exit from a function before the end or to return a value from a function, you can use the return expression :

    function odd( x : Int ) : Bool {
        if( x % 2 != 0 )
            return true;
        return false;
    }

The return expression can be used without an argument if the function does not require a value to be returned :

    function foo() : Void {
        // ...
        if( abort )
            return;
        // ....
    }

Break and Continue

These two keywords are useful to exit early a for or while loop or to go to the next iteration of a loop :

    var i = 0;
    while( i < 10 ) {
        if( i == 7 )
            continue; // skip this iteration.
            // do not execute any more statements in this block, 
            // BUT go back to evaluating the "while" condition.
        if( flag )
            break; // stop early.
            // Jump out of the "while" loop, and continue 
            // execution with the statement following the while loop.
    }

Exceptions

Exceptions are a way to do non-local jumps. You can throw an exception and catch it from any calling function on the stack :

    function foo() {
        // ...
        throw "invalid foo";
    }

    // ...

    try {
        foo();
    } catch( e : String ) {
        // handle exception
    }

There can be several catch blocks after a try, in order to catch different types of exceptions. They're tested in the order they're declared. Catching Dynamic will catch all exceptions :

    try {
        foo();
    } catch( e : String ) {
        // handle this kind of error 
    } catch( e : Error ) {
        // handle another kind of error
    } catch( e : Dynamic ) {
        // handle all other errors
    }

All the try and the catch expressions must have the same return type except when no value is needed (same as if).
See Handling Exceptions for more info.

Switch

Switches are a way to express multiple if...else if... else if test on the same value:

    if( v == 0 )
        e1
    else if( v == foo(1) )
        e2
    else if( v == 65 || v == 90 )
        e3
    else
        e4;

Will translate to the following switch :

    switch( v ) {
    case 0:
        e1;
    case foo(1):
        e2;
    case 65, 90:
        e3;
    default:
        e4;
    }

Note: In the example above, a case statement reads "65, 90". This is an example where a case expects to match either of the two (or several) values, listed as delimited by comma(s).

Switches in Haxe are different from traditional switches : all cases are separate expressions so after one case expression is executed the switch block is automatically exited. As a consequence, break can't be used in a switch and the position of the default case is not important.

On some platforms, switches on constant values (especially constant integers) might be optimized for better speed.

Switches can also be used on enums with different semantics. It will be explained later in this document.

Local Functions

Local functions are declared using the function keyword, but they can't have a name. They're values just like literal integers or strings :

    var f = function() { /* ... */ };
    f(); // call the function

Local functions can access their parameters, the current class statics and also the local variables that were declared before it :

    var x = 10;
    var add = function(n) { x += n; };
    add(2);
    add(3);
    // now x is 15

However, local functions declared in methods cannot access the this value. To access "this", you need to declare a local variable such as me :

    class C {

        var x : Int;

        function f() {
            // WILL NOT COMPILE
            var add = function(n) { this.x += n; };
        }

        function f2() {

            // will compile
            var me = this;
            var add = function(n) { me.x += n; };
        }
    }

Anonymous Objects

Anonymous objects can be declared using the following syntax :

    var o = { age : 26, name : "Tom" };

Please note that because of type inference, anonymous objects are also strictly typed.

«« Basic Types - Type Inference »»

version #19683, modified 2013-08-22 23:41:08 by tarwin