Skip to content

Latest commit

 

History

History
123 lines (81 loc) · 8.45 KB

3.6.类型推断.md

File metadata and controls

123 lines (81 loc) · 8.45 KB

3.6.类型推断

类型推断的效果将会频繁出现于整个文档中。一个例子可以展示类型推断:

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 prints Unknown<0>. This is a monomorph, a type that is not yet known. The next line x = "foo" assigns a String literal to x, which causes the unification of the monomorph with String. We then see that the type of x has changed to String.

每当一个不同于 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 empty Array. At this point, we can tell that the type of x is an array, but we do not yet know the type of the array elements. Consequently, the type of x is Array<Unknown<0>>. It is only after pushing a String onto the array that we know the type to be Array<String>.

3.6.1.由上而下推断

大多数时候,类型会被自行推断后可能被统一为一个预期类型。然而在一些地方一个预期类型可能会被用来影响推断,此时我们称其为自上而下的推断。

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 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.

在介绍构造泛型类型参数(第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");
  }
}

显式的类型声明 Stringhaxe.Template 在此处被用于确定 make 的返回类型。能这么做是因为我们通过 make() 调用该方法,所以知道返回类型会被赋值到变量上。利用这个信息,我们可以把 未知类型(Unknown Type)T 分别绑定至 Stringhaxe.Template 类型上 。

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.

3.6.2.局限

类型推断在使用局部变量的时候节省了大量手动类型化,但是有时类型系统仍然需要一些帮助。事实上,它甚至不尝试推断变量(第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.