Type Inference

Introduction


Haxe can determine the type of a variable based on the value assigned to that variable. This feature, known as type inference, means you receive all the benefits of strong typing without needing to explicitly define the type of each variable.

For example, the following code:

var f = 10.5;
var s = "foo";
var arr = ["hello", "world"];
will be compiled as if you had written:
var f : Float = 10.5;
var s : String = "foo";
var arr : Array<String> = ["hello", "world"];  

If you'd like to know which type the compiler inferred at compile time you can use the special identifier '$type'. This can be useful when debugging.

We'll use '$type' in the rest of this document to demonstrate how the compiler infers types.

Printing a Type

Anywhere in your program, you can use the $type operation to know the type of a given expression. At compilation, the $type operation will be removed and only the expression will remain :

    var x : Int = $type(0);

This will print the line number together with 'Warning : Int' at compilation, and still compile the same program as if $type was not used.

This is useful to quickly get a type instead of looking at the class or some documentation.

Local variable inference


Type Inference means that the type information is not only checked in the program, it's also carried when typing, so it doesn't have to be resolved immediately. For example a local variable can be declared without any type (it will have the type Unknown) and when first used, its type will be set to the corresponding one. Type Inference enables the whole program to be strictly typed without any need to put types everywhere. In particular, local variables do not need to be typed, their types will be inferred when they are first accessed for reading or writing :
    var loc;
    $type(loc); // print Unknown<0>
    loc = "hello";
    $type(loc); // print String

Function types inference

Declaring the type of a parameter in a class method or local function is also optional. The first time the function is used, the type of the parameter will be set to the type of the argument it was used with, just like local variables. This can be tricky since it will depend on the order in which the program is executed. Here's an example that shows the problem :

    function f( posx ) {
        // ....
    }
    // ...

    f(134);
    f(12.2); // Error : Float should be Int

The first call to f sets the type of posx to Int. The second call to f causes a compilation error because f is now expecting an Int, not a Float. However if we reverse the two calls to f, the value type is set to Float first. A second call using an Int does not fail since Int is a subtype of Float.

    function f( posx ) {
        // ....
    }
    // ...
    
    f(12.2); // Sets the parameter type to Float
    f(134); // Success

In this example the two calls are near each other so it's quite easy to understand and fix. In larger programs with more complex cases, fixing such compilation problems can be tricky. The easiest solution is to explicitly set the type of the function. Then the call that was responsible for the problem will be displayed when recompiling.

Drawing from the first example:

    function f( posx : Int ) {
        // ....
    }
    // ...

    f(134);
    f(12.2); // Failure will point to this line

Inference and type parameters

function f<T>(p: Class<T>): T
{

}

function g(p: Float)
{

}

var a: Dynamic = {};

var b = f(a);

$type(b); //At this point the type of 'b' is not inferred and is traced as 'Unknown<0>'

g(b);

$type(b); //Type of 'b' is from now on known to be 'Float'

The code above illustrates a situation with a function call that initializes a variable declared without type - the type inferring does not occur at that point, because the combination of the function being called and call parameters makes it impossible. The 'type parametrized' function f depends on its first parameter of type Class<T> (T being an abstract, see the Type parameters doc. page), to decide what type it returns. When passed a Dynamic object however, it is clear that the type parameter 'T' makes no sense and is unknown. Type inferring is delayed until further, when a call to g that takes a Float is able to tell the compiler to specify type of b as Float. Hence the first $type statement traces "Unknown<0>" and the second - "Float". After the type is inferred, it is naturally immutable.

User Choice

Using type inference is a choice. You can choose to simply not type your variables and functions and let the compiler infer the types for you, or you can type all of them in order to have more control over the process. The best may be in the middle, by adding some typing in order to improve code documentation and still be able to quickly write some functions without typing everything.

In all cases, and unless you use dynamics (they will be introduced later), your program will be strictly typed and any wrong usage will be detected instantly at compilation.

«« Syntax - Object Oriented Programming »»

version #18021, modified 2013-03-29 08:13:41 by justsee