3.6.1 Top-down Inference

Most of the time, types are inferred on their own and may then be unified with an expected type. In a few places, however, an expected type may be used to influence inference. We then speak of top-down inference.

Define: Expected Type

Expected types occur when the type of an expression is known before that expression has been typed, such as when the expression is an argument to a function call. They can influence typing of that expression through top-down inference.

A good example is an array of mixed types. As mentioned in Dynamic, the compiler refuses [1, "foo"] because it cannot determine an element type. Employing top-down inference, this can be overcome:

class Main {
  static public function main() {
    var a:Array<Dynamic> = [1, "foo"];
  }
}

Here, the compiler knows while typing [1, "foo"] that the expected type is Array<Dynamic>, so the element type is Dynamic. Instead of the usual unification behavior where the compiler would attempt (and fail) to determine a common base type, the individual elements are typed against and unified with Dynamic.

We have seen another interesting use of top-down inference when the construction of generic type parameters was introduced:

import haxe.Constraints;

class Main {
  static public function main() {
    var s:String = make();
    var t:haxe.Template = make();
  }

  @:generic
  static function make<T:Constructible<String->Void>>():T {
    return new T("foo");
  }
}

The explicit types String and haxe.Template are used here to determine the return type of make. This works because the method is invoked as make(), so we know the return type will be assigned to the variables. Utilizing this information, it is possible to bind the unknown type T to String and haxe.Template respectively.