Structures

Structures (also named anonymous objects) are a simple list of fields which does not refer to a specific class instance.

You can create structure values by using the following syntax :

var point = { x : 1, y : -5 };

Please note that you can have quite complex structures and that they can be recursive :

var user = {
    name : "Nicolas",
    age : 32,
    pos : [{ x : 0, y : 0 },{ x : 1, y : -1 }],
};

Each structure have its corresponding type which correspond the the type of all its fields. For instance the type of the point value, is { x : Int, y : Int } and the type of the user value is the following :

{ name : String, age : Int, pos : Array<{ x : Int, y : Int }> }

Because of type inference, you don't need to type your local variables structures, but you will have to type member fields of your classes, for instance :

class Path {
    var start : { x : Int, y : Int };
    var target : { x : Int, y : Int };
    var current : { x : Int, y : Int };
}

Of course, since you might not want to rewrite every time all your structure types, you can use a typedef to create an alias and give your structure type a proper name :

typedef Point = { x : Int, y : Int }

class Path {
    var start : Point;
    var target : Point;
    var current : Point;
}

Structural Subtyping

One very important feature of structures is that it is possible to hide some fields. For instance let's look at these two structure types definitions :

typedef Point = { x : Int, y : Int }
typedef Point3D = { x : Int, y : Int, z : Int }

Because Point3D has all the fields which are required by Point, it means that every Point3D value is also a Point. It means that the following code will compile :

var p3d : Point3D = { x : 0, y : 0, z : 0 };
var pt : Point = p3d;

However please note that if the p3d variable is not typed as Point3D, the compiler will complain saying that there is an extra field z : this is because this field is not required by Point and is then useless.

Classes instances

The structural subtyping works between two structures, but it also works between a class instance and a structure. Every class instance that match the structure fields will be allowed to be passed in place a structure is required. For instance the following class :

class CPoint {
    public var x : Int;
    public var y : Int;
    public function new(x,y) {
        this.x = x;
        this.y = y;
    }
    public function add( p : Point ) {
        x += p.x;
        y += p.y;
    }
}

...can be used where a structure is required :

function foo( p : Point ) {
    // ...
}
foo( new CPoint(0,0) );

Please note that the class fields that are required by the structure must be public

Type inference

Literal structure values are correctly inferred based on the fields that they declare, but it is also possible to use type inference to build a structure based on the fields which are accessed, such as the following example shows :

function foo(pt) {
    return pt.x + pt.y;
}

Because of type inference, the pt argument will be correctly typed as { x : Int, y : Int }.

About Performances

For platforms that don't have a strictly typed runtime, such as Javascript, PHP or Neko, structures offer very good performances.

However for platforms that after a strictly typed runtime, such as Flash, C++, C# and Java, each access to a structure field will perform a dynamic lookup. Structures will then be slower than classes instances on these platforms. However you shouldn't worry to much about it unless you write performance-critical code.

Optional Fields

New in 2.09

A structure field can be optional by using a question mark before its name :

function foo( pt : { ?x : Int, ?y : Int }) {
    // ...
}
foo({ x : 1, y : 1 });
foo({ x : 1 });
var tmp = {};
foo(tmp);

Optional structure fields are types are nullable (typed as Null<Int> in our example).

Please note that optional fields only work for literal structure values (ie structures which have just been created and are not been explicitly typed).

The rationale behind that is that because it is possible to hide fields, it would break type-safety if we would allow arbitrary structures, as the following example shows :

var mixed : { x : String, y : Int } = { x : "Hello", y : 0 };
var yonly : { y : Int } = mixed; // hide x
foo(yonly); // x is String !!!

JSON notation

New in 2.09

It is also possible to use JSON notation for structure value fields :

var point = { "x" : 1, "y" : -5 };

You can have arbitrary data inside the field name : this field will still be part of the inferred structure type but only if it's a valid Haxe identifier. If it's not a valid identifier, you will have to use Reflect.field to read it and Reflect.setField to write it.

It is not possible to use JSON notation in structure types, it's only allowed for values.

Class notation

It is also possible to declare structure types as a list of class fields, by using the same syntax as a class body :

typedef Point = {
    var x : Int;
    var y : Int;
}

The structure class notation allows in particular to define properties and methods as part of a structure definition.

In order to declare optional structure fields using class notation, you need to add the @:optional Metadata to it. For instance in the following example both x and y fields are optional :

typedef Point = {
    @:optional var x : Int;
    @:optional var y : Int;
    function add( p : Point ) : Void;
}

Function in Structures Types

If you want to add a function as part of your structure, you have several possibilities. The first and the most easy one is to use a function-type :

typedef Point = {
    x  : Int,
    y  : Int,
    add : Point -> Void,
}

However in that case you will not be able to pass a class instance containing an add method a structure. This is because in classes methods are read-only, they cannot be modified, whereas this Point definition declare a variable of type function which can be modified.

The solution is then to use class notation to declare a proper method instead of a normal field :

typedef Point = {
    var x : Int;
    var y : Int;
    function add( p : Point ) : Void;
}

version #12655, modified 2012-03-29 14:55:29 by ncannasse