类型推断的效果将会频繁出现于整个文档中。一个例子可以展示类型推断:
The effects of type inference have been seen throughout this document and will continue to be important. A simple example shows type inference at work:
class Main {
public static function main() {
var x = null;
$type(x); // Unknown<0>
x = "foo";
$type(x); // String
}
}
这里有个特殊的 $type
指令在之前为了便于 函数类型(2.6) 的说明稍有提及,现在让我们来正式地介绍它:
The special construct $type was previously mentioned in order to simplify the explanation of the Function Type (Section 2.6) type, so let us now introduce it officially:
定义 $type: $type 是一个可以像函数一样调用的编译时机制,它接受一个参数。编译器执行参数的表达式并输出表达式的类型。
[warning] Construct: $type $type is a compile-time mechanism being called like a function,with a single argument. The compiler evaluates the argument expression and then outputs the type of that expression.
在上面的例子中,第一个 $type
输出 Unknow<0>
。这是一个 单形(第2.9节),一个还不知道其类型的类型。下一行 x = "foo"
将一个 String
类型的字面值赋值到变量 x
上,于是触发了这个单形与 String
之间的一致性检查 。然后我们可以看到 x
的类型被改变为 String
类型了 。
In the example above, the first
$type
printsUnknown<0>
. This is a monomorph, a type that is not yet known. The next linex = "foo"
assigns aString
literal tox
, which causes the unification of the monomorph withString
. We then see that the type ofx
has changed toString
.
每当一个不同于 Dynamic(第2.7节)的类型被统一为一个单形时,这个单形便会变形(morph)为该类型,并且在此之后它就不能再变形为另外的类型了。这一特性正如其名字中的 mono(单一的)所表达的一样。
Whenever a type other than Dynamic is unified with a monomorph, that monomorph morphs into that type, or in simpler terms, becomes that type. Therefore, it cannot morph into a different type afterwards, a property expressed in the mono part of its name.
遵循一致性检查的规则,类型推断可以在复合类型中触发:
Following the rules of unification, type inference can occur in compound types:
class Main {
public static function main() {
var x = [];
$type(x); // Array<Unknown<0>>
x.push("foo");
$type(x); // Array<String>
}
}
变量 x
在一开始被初始化为一个空的 Array
数组。此时我们除了知道变量 x
是一个数组外并不知道其数组元素的具体类型。此时 x
的类型必然为 Array<Unknown<0>>
。只有当我们为其推入一个 Sring
类型元素之后才知道其类型为 Array<String>
。
Variable
x
is first initialized to an emptyArray
. At this point, we can tell that the type ofx
is an array, but we do not yet know the type of the array elements. Consequently, the type ofx
isArray<Unknown<0>>
. It is only after pushing aString
onto the array that we know the type to beArray<String>
.
大多数时候,类型会被自行推断后可能被统一为一个预期类型。然而在一些地方一个预期类型可能会被用来影响推断,此时我们称其为自上而下的推断。
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.
定义 预期类型: 当表达式被类型化之前,表达式所处位置的类型是已知的,称该类型为预期类型,例如,当一个表达式作为一个函数调用的参数传递时,参数类型可以通过所谓的自上而下推断来影响表达式的类型化过程。
[warning] Definition: Expected Type Expected types occur when the type of an expression is known before that expression has been typed, e.g. because the expression is argument to a function call. They can influence typing of that expression through what is called top-down inference (3.6.1).
一个很好的例子是混合类型的数组。如在 Dynamic(第2.7节)中提到的,编译器不允许 [1,"foo"] ,因为编译器无法确定元素的类型。但是通过自上而下的推断可以绕过这个问题:
A good example are arrays of mixed types. As mentioned in Dynamic (Section 2.7), 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"];
}
}
这里,编译器知道类型化 [1,"foo"] 时的预期类型是 Array<Dynamic>
,因此元素类型为 Dynimic
,不同于常规的一致性检查行为下编译器会尝试(此例会失败)去寻找一个通用基本类型(第3.5.5节),此时每一个元素都会被统一至并类型化为 Dynamic
类型。
Here, the compiler knows while typing
[1, "foo"]
that the expected type isArray<Dynamic>
, so the element type isDynamic
. 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 withDynamic
.
在介绍构造泛型类型参数(第3.3.1节)时,我们看到了另一个自上而下推断的有趣用法
We have seen another interesting use of top-down inference when construction of generic type parameters (3.3.1) 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");
}
}
显式的类型声明 String
与 haxe.Template
在此处被用于确定 make
的返回类型。能这么做是因为我们通过 make()
调用该方法,所以知道返回类型会被赋值到变量上。利用这个信息,我们可以把 未知类型(Unknown Type)T
分别绑定至 String
和 haxe.Template
类型上 。
The explicit types
String
andhaxe.Template
are used here to determine the return type ofmake
. This works because the method is invoked asmake()
, so we know the return type will be assigned to the variables. Utilizing this information, it is possible to bind the unknown typeT
toString
andhaxe.Template
respectively.
类型推断在使用局部变量的时候节省了大量手动类型化,但是有时类型系统仍然需要一些帮助。事实上,它甚至不尝试推断变量(第4.1节)或者属性(第4.2节)字段,除非它有一个直接的初始化。
Type inference saves a lot of manual type hints when working with local variables,but sometimes the type system still needs some help. In fact, it does not even try to infer the type of a variable (4.1) or property (4.2) field unless it has a direct initialization.
也有一些情况递归调用,当类型推断有限制的时候。如果一个函数递归调用它自身,它的类型还不(完全)不知道,类型推断可能推断一个错误,太详细的类型。
There are also some cases involving recursion where type inference has limitations. If a function calls itself recursively while its type is not (completely) known yet, type inference may infer a wrong, too specialized type.
一种不同类型的限制涉及代码的可读性。如果类型推断过度使用,可能由于可见类型的缺失而难于理解程序的部分。特别是在方法签名。建议在类型推断和显式类型中找到一个好的平衡。
A different kind of limitation involves the readability of code. If type inference is overused it might be difficult to understand parts of a program due to the lack of visible types. This is particularly true for method signatures. It is recommended to find a good balance between type inference and explicit type hints.