3.2 Type Parameters

Haxe allows parametrization of a number of types, as well as class fields and enum constructors. Type parameters are defined by enclosing comma-separated type parameter names in angle brackets <>. A simple example from the Haxe Standard Library is Array:

class Array<T> {
  function push(x : T) : Int;
}

Whenever an instance of Array is created, its type parameter T becomes a monomorph. That is, it can be bound to any type, but only one at a time. This binding can happen

  • explicitly by invoking the constructor with explicit types (new Array<String>()) or
  • implicitly by type inference, e.g. when invoking arrayInstance.push("foo").

Inside the definition of a class with type parameters, these type parameters are an unspecific type. Unless constraints are added, the compiler has to assume that the type parameters could be used with any type. As a consequence, it is not possible to access fields of type parameters or cast to a type parameter type. It is also not possible to create a new instance of a type parameter type, unless the type parameter is generic and constrained accordingly.

The following table shows where type parameters are allowed:

Parameter on Bound upon Notes
Class instantiation Can also be bound upon member field access.
Enum instantiation
Enum Constructor instantiation
Function invocation Allowed for methods and named local lvalue functions.
Structure instantiation

With function type parameters being bound upon invocation, such a type parameter (if unconstrained) accepts any type. However, only one type per invocation is accepted. This can be utilized if a function has multiple arguments:

class Main {
  static public function main() {
    equals(1, 1);
    // runtime message: bar should be foo
    equals("foo", "bar");
    // compiler error: String should be Int
    equals(1, "foo");
  }

  static function equals<T>(expected:T, actual:T) {
    if (actual != expected) {
      trace('$actual should be $expected');
    }
  }
}

Both arguments expected and actual of the equals function have type T. This implies that for each invocation of equals the two arguments must be of the same type. The compiler admits the first call (both arguments being of Int) and the second call (both arguments being of String) but the third attempt causes a compiler error.

Trivia: Type parameters in expression syntax

We often get the question why a method with type parameters cannot be called as method<String>(x). The error messages the compiler gives are not very helpful. However, there is a simple reason for that: The above code is parsed as if both < and > were binary operators, yielding (method < String) > (x).