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.