Basic Types

Haxe provides several basic types:

  • Int and Float, for numeric values.
  • Bool for boolean/logical values, which may be either true or false.

Numeric Types


Haxe provides two basic numeric types: Int for integer values and Float for floating-point values.

Arithmetic operations (+, -, *, and /) between these two numeric types are conservative (adding two Int together gives an Int), except for division which always returns a Float.

Bitwise operators (<<, >>, >>>, &, |, ^, ~) may only be used on two Ints, and subsequently return an Int''.

Conversion

Every Int value can be used at the place a Float value is otherwise required.

On the other hand, in order to convert an Float to an Int you may use one of the following functions:

  • Std.int will round towards zero, usually the fastest operation
  • Math.round will round to the nearest integer
  • Math.ceil will round towards positive infinity
  • Math.floor will round towards negative infinity

Overflows

For performance reasons, Haxe does not enforce any overflow behavior. It's up to the target platforms to worry about that.

Here's some notes about the way overflows are performed per-platform :

  • C++, Java, C# and Neko use 32-bit integer values, with the usual overflow practices
  • Flash AVM2 also uses 32-bit integers, but higher integers are memory-boxed so they may be slower
  • PHP, Javascript, and Flash 8 do not have native Int values, so overflow will only occur if the value reach the float limit (2^52 for double). In this case, however, all bitwise operations will result in a 32-bit overflow.

You can use the haxe.Int32 and haxe.Int64 classes to perform operations that correctly overflow on both 32 and 64 bits, regardless of the target platform.

Nullability

To preserve the target's native behavior while maintaining performance, the default values of of certain types may vary from target to target. The reason is that Haxe want to get the best speed for each platform, and then needs to make a few compromises:

  • Making all type nullable would slowdown static platforms a lot
  • Making sure all null values are transformed into the corresponding default value would slowdown dynamic platforms a lot

So instead of enforcing a single behavior, we prefer to specify how Haxe will behave, so developers can plan or adapt their code accordingly. The compiler error messages will also greatly help in the transition between different types of platforms.

Dynamically-Typed Platforms

On dynamically-typed platforms (Javascript, PHP, Neko, Flash 8) : every value can be null, and is null by default if not initialized. You can freely test for null equality without having to provide extra type information.

Statically-Typed Platforms

On statically-typed platforms (Flash, C++, Java, C#), basic types have their own default values :

  • every Int is by default initialized to 0
  • every Float is by default initialized to NaN on Flash9+, and to 0.0 on CPP, Java and C#
  • every Bool is by default initialized to false

It is not possible to store a null value into a basic type, unless you type it using Null<T> modifier :

var a : Int = null; // error on static platforms
var b : Null<Int> = null; // allowed

It is also not possible to compare a basic type value, unless you type it using Null<T> modifier :

var a : Int = 0;
if( a == null ) { ... } // error on static platforms
var b : Null<Int> = 0;
if( b != null ) { .... } // allowed

If you assign a Null<T> (or a Dynamic) value which contains null to the corresponding (unnullable) basic type, you will get the default value instead :

var n : Null<Int> = null;
var a : Int = n;
trace(a); // 0 on static platforms

Optional Parameters and Nullability

Optional parameters have also a specific behavior with regards to nullability.

In particular, we have to be able to make the difference between native optional parameters which are not nullable and Haxe ones which might be nullable. This diffence is made by the usage of a question-mark optional parameter :

// x is Int (not nullable)
function foo( x : Int = 0 ) {...}
// y is Null<Int> (nullable)
function bar( ?y : Int ) {...}
// z is also Null<Int>
function opt( ?z : Int = -1 ) {...}

In the case the optional parameter is nullable, then we can pass it null and check for null in all cases. If we omit the parameter when calling the method, the default value will be use (if any exist) or null instead.

In the case the optional parameter is not nullable, then we cannot pass null or check for null on static platforms. If we omit the parameter when calling the method, the default value will be used anyway.

Impact on Standard Library

The standard library correctly type the cases when a nullable value can be returned, for instance Hash.get will return Null<T> and you can safely check for null after reading from a hash table :

var h : Map<String, Int> = new Map();
var x = h.get("hello");
trace(x); // null on all platforms

However please note that Hash.set will only be able to set the exact type specified in the hash table :

var h : Map<String, Int> = new Map();
h.set("hello",null); // error on static platforms
var h2 : Map<String, Null<Int>> = new Map();
h2.set("hello",null); // allowed

Array is another case that you need to be careful with : every access outside its allocated range will return the default value for the basic type, unless of course the content of the Array is nullable :

var a = new Array<Int>();
trace(a[5]); // 0 on static platforms
a.push(null); // not allowed on static platforms
var b = new Array<Null<Int>>();
trace(b[5]); // null
b.push(null); // allowed

Impact on cross-platformability

There are two cases to consider when porting some code here.

Porting some code from a dynamic platform to a static one (from JS to CPP for instance) is quite easy : every place you compare with a null you will get an error message and all you will have to do is to set the corresponding basic type to be Null :

var x : Int = 0;
...
if( flag ) x = null; // error

Can be changed to :

var x : Null<Int> = 0;
...

Porting some code from a static platform to a dynamic one require a bit more work : it will require you to ensure that all object member variables are correctly initialized to their default value in the class constructor :

class Point {
    var x : Int;
    var y : Int;
    public function new() {
        // ensure correct initialization on dynamic platforms
        x = y = 0; 
    }
}

You will also have to make sure that you don't check for == 0 when for instance reading outside of an Array. In that case you can either make the Array contain nullable value, or if you want to get best performances and can be sure that your code logic will not store any 0 value in the array use conditional compilation to perform the check depending on the platform :

static inline var OUTSIDE : Int = #if js null #else 0 #end;

var a = new Array<Int>();
var out : Int = a[5];
if( out == OUTSIDE ) {
  ....
}

Impact on Speed

Please be aware than using Null modifier will make the manipulation of this value slower because it will usually require allocating some memory block to be able to differentiate between a null and an actual real value. However this will usually be more optimized than trying to emulate the same behavior by yourself.

version #19849, modified 2013-12-16 16:43:23 by seeker1983