devboy

Understanding function types in haXe

function

When you are an ActionScript 3 developer, like me, you might wonder while reading the title of this post: Function types? They’re just typed as Function! Oh, and types? Plural? There is only one kind of function in my code!
That might be true for ActionScript, also JavaScript but in haXe you have a lot more information about your functions and more control over the type of them.

The haXe type-system might be one of the biggest differences to ActionScript 3 whereas the rest of the language and especially it’s syntax is very similar. To fully understand function types in haXe we first have to cover some ground on the haXe type-system itself and it’s features like type inference.

Type inference

Type inference does actually exactly what it name says, if you’re not a native English speaker like me, let’s have a look at the meaning of “inference” in the dictionary:

inference |ˈinf(ə)rəns|

a conclusion reached on the basis of evidence and reasoning.

Applying this to our haXe code will mean that the type of a variable will be concluded on the basis of evidence, but what is the evidence in this case? Pretty simple, it’s the first value you assign to the variable.
Let’s have a look at some code:

var x; // type of x is Unknown
    x = "I'm a String"; // type of x is set to String
    x = 1; // this will cause a compiler error as Int cannot be assigned to String

view raw typeInf.as This Gist brought to you by GitHub.

You can see that once you assign a value, the variables type is fixed to the type of the assigned value. This will save you some typing, especially for local variables, while in other languages like ActionScript 3 you would have to set the type manual for each variable.
Type inference is not mandatory, you can of course type your variables before you assign a value to them, just like in ActionScript:

var x: String; // type is now set to String
    x = "I'm a String"; // this will work
    x = 1; // this will cause an error

There are still a lot of use cases for setting types manually, like typing a variable to an interface.

Types will also be inferred for function parameters and return values:

function inferred(x) {
    return x;
}

inferred("abc"); // will take the string and return it
inferred(1); // will throw a compiler error because x is typed to String now

Function types

Now that we know that haXe can infer the type of function parameters and return values, we can finally start talking about function types. As for type inference, parameter and return types are the building blocks of the type of a function, let’s have a look at a simple square function that takes a number and multiplies it by itself.

function square( x: Float ) {
    return x * x;
}
view raw square.as This Gist brought to you by GitHub.

We’ve set the parameter “x” to the type Float, and the type of the return value will be inferred to Float as well. Let’s store it in a local variable and ask the haXe compiler for its type:

var square = function(x: Float) {
return x * x;
}

type(square); // type of square is Float -> Float
view raw type.as This Gist brought to you by GitHub.

So the type is Float -> Float , let’s decipher this: The first Float is the parameter x and the second Float is the return value which is type inferred to Float. To make this a little clearer, let’s have a look at a function that takes two parameters:

var getStringXTimes = function(x: Int, string: String) {
var result = "";
for ( i in 0...x )
result += string;
return result;
}

type(getStringXTimes); // Int -> String -> String

Now the type is Int -> String -> String  , which let’s us conclude that list of types seperated by -> contains of all parameter types and ends with the type of the return value.

This lets us now do something very useful, we can now set our variable to a certain function type, for example a type that taks an Int parameter an returns a Bool:

var biggerThan2: Int -> Bool = function(x) {
return x > 2;
}

This will now give us compile time checking for function types, so no stumbling in the dark when it comes to passing functions around!

function doMath( x: Float, y: Float, mathOperation: Float -> Float -> Float ) {
return mathOperation(x, y);
}

var addition = function(x: Float, y: Float) {
return x + y;
}

var square = function(x: Float) {
return x * x;
}

type( doMath ); // Float -> Float -> (Float -> Float -> Float) -> Float

doMath( 1, 2, addition ); // will return 3
doMath( 1, 2, square ); // compiler error: Float -> Float should be Float -> Float -> Float

That’s just plain awesome! Isn’t it? We can now say goodbye to runtime errors!

Type parameters

As haxe has support for generics, you might want to type some of your parameters or the return value dynamically, depending on the type of a parameter. Let’s say we want to create a function that deletes items of an array based on the result of a function you pass as a parameter. Arrays in haXe are like Vectors in ActionScript 3, it can only contain items of a single type.
The first idea would be to just assign a dynamic type to your function parameters, like you would do in ActionScript 3 (Dynamic is equal to the * type in AS3):

function array_delete_if( array: Array<Dynamic>, processor: Dynamic-> Bool ):Array<Dynamic>
{
for ( item in array )
if ( processor(item) )
array.remove( item );
return array;
}

But this is not very useful as we are gonna loose compile time checking. For example when passing a function that takes an Int as parameter instead of the String that is in the array. But haXe wouldn’t be haXe if it wouldn’t provide us with a nice solution: We can add a type parameter to the function which will resolve the type of the values stored in the array, store it in the value T, which then can be used to describe the other types of the function parameters and return value.

With this we will get nice compile time checking for our function!

var ints = [1, 2, 3]
var strings = ["a", "bb", "ccc"];

var biggerThan1 = function(x:Int) {
return x > 1;
}

var longerThan1 = function(x:String) {
return x.length > 1;
}

function array_delete_if<T>( array: Array<T>, processor: T-> Bool ):Array<T>
{
for ( item in array )
if ( processor(item) )
array.remove( item );
return array;
}

array_delete_if(ints, biggerThan1); // [ 1 ]
array_delete_if(strings, longerThan1); // [ "a" ]
view raw typeParams.as This Gist brought to you by GitHub.

9 Responses to Understanding function types in haXe

  1. Mick says:

    Really helpful !

  2. mike cann says:

    Great post mate, keep em coming!

  3. Sven Dens says:

    You’re a fast learner Dominic :-)
    Very nice post! Congrats, and have fun studying & experimenting with haXe!

  4. Alec McEachran says:

    Hey Dominic,

    Great post! I personally found the Int->Int->Int really confusing, expecting (Int,Int)->Int, but I guess there’s method in Nicolas’ madness (I just don’t know what it is).

    Hope you get a chance to use this stuff in anger soon! ;)

    Cheers,
    Alec

  5. devboy says:

    @alec Thanks, I was also expecting it to be something like (Int,Int)->Bool at first. Thats how I know it from Scala: (Int,Int)=>Boolean.
    But both ways make sense in some way, though the Scala way might be tiny bit simpler to read.

  6. Zorglug says:

    @alec It’s the same signature as in Haskell. Since they are pretty similar, I will assume this is the same in OCaml, which is the language used to write the haXe compiler. That’s probably the source of inspiration for that syntax.

  7. thienhaflash says:

    Great post :) man.

    Just like Alec, i can’t figure out why should it be float -> float -> float instead of (float,float) -> float as i see the later is much more readable, especially for nested funtion parameters like the example above. -> should only be one kind of meaning : return (it means from function) and (,,,) should be parameters, right ? any idea ?

  8. devboy says:

    It makes sense when looking at OCaml function types and how you write them: http://xahlee.org/ocaml/functions.html

  9. saumya says:

    Nice read. A very good insight of the haXe language as a whole I would say. Thanks for sharing.

Leave a Reply

devboy

Connect

Follow me on Twitter
Connect with me on Linked In