Basic Types
Haxe provides several basic types :
IntandFloatfor numeric values (read below)Boolfor booleans (can be eithertrueorfalse)
Numeric Types
Haxe provides two basic numeric types :
Int for integer values and Float for floating-point values.
Numerical operations +, -, *, / between these two numeric types are conservative (adding two Int together gives an Int) expect for the divide operator which always return a Float.
Bitwise operations <<, >>, >>>, &, |, ^, ~ only operates on two Int and return an Int.
Conversion
Every Int value can be used at the place a Float value is otherwise required.
OTOH in order to convert an Float' to an Int you need to use one of the following methods :
Std.int: will round towards zero, usually the fastest operationMath.round: will round to the nearestMath.ceil: will round towards positive infinityMath.floor: will round towards negative infinity
Overflows
In order to preserve the best available performance, Haxe does not enforce any kind of overflow behavior, it is then platform-dependent.
Here's some notes about the way overflows are performed per-platform :
- CPP, Java and C# are using real 32-bits int values, with the corresponding overflow
- Flash AVM2 is also using real 32-bits values but higher integers are memory-boxed so slower
- PHP, JS and Flash8 does not have native Int values, so the overflow will only occur if the value reach the float limit (2^52 for double). However all bitwise operations will result in a 32-bits overflow
- Neko has 31-bits int values in order to differentiate between unboxed integers and boxed pointers. Please note that you still have 1 bit of sign and 30 bits of data, so the maximum positive integer is 2^30-1 = 0x3FFFFFFF and the maximum negative integer is -2^30 = 0xC0000000
You can use the haxe.Int32 and haxe.Int64 api to perform operations which correctly overflow on both 32 and 64 bits, whatever the platform it's running on.
Nullability
In order to keep native behavior and avoid performances problems, Haxe specification allow two behaviors for basic types concerning their ability to be ''null'.
The reason behind this differences is that Haxe want to get the best speed for each platform, and then needs to make a few compromises :
- making all type nullables would slowdown static platforms a lot
- making sure all
nullvalues 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.
Dynamic Platforms
On dynamic platforms (JS,PHP,Neko,Flash8) : 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.
Static Platforms
On static platforms (Flash, CPP, Java, C#), basic types have their own default values :
- every
Intis by default initialized to 0 - every
Floatis by default initialized toNaNon Flash9+, and to 0.0 on CPP, Java and C# - every
Boolis by default initialized tofalse
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 = new Hash<Int>(); 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 = new Hash<Int>(); h.set("hello",null); // error on static platforms var h2 = new Hash<Null<Int>>(); h.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.