From the docs:
注意:HHVM允许像
$x = Vector<int>{5,10};
这样的语法,但是Hack在这种情况下禁用语法,而选择推断它。
这有什么特别的原因吗?这不是违反了快速失败规则吗?
在某些情况下,这会导致错误被延迟,从而导致更困难的回溯。
例如:<?hh // strict
function main() : void {
$myVector = new Vector([]); // no generic syntax
$myVector->addAll(require 'some_external_source.php');
}
上面的代码不会导致错误,直到它被用在静态类型集合实际存在的上下文中:
class Foo
{
public ?Vector<int> $v;
}
$f = new Foo();
$f->v = $myVector;
现在,如果向量包含int
以外的东西,则会出现错误。但是必须追溯错误,直到有缺陷的数据被实际导入的地方。如果可以首先使用泛型语法实例化vector,则不需要这样做:
$myVector = new Vector<int>([]);
$myVector->addAll(require 'some_external_source.php'); // fail immediately
我在Facebook从事Hack类型系统和类型检查工作。这个问题在facebook内部已经被问过几次了,最好有一个好看的、外部可见的地方把答案写下来。
首先,你的问题以以下代码为前提:
<?hh // strict
function main() : void {
$myVector = new Vector([]); // no generic syntax
$myVector->addAll(require 'some_external_source.php');
}
然而,由于在顶层之外使用require
,该代码没有通过类型检查器,因此在HHVM上实际执行它的任何结果都是未定义的行为,使得整个讨论对该代码没有意义。
但是对于其他可能进行类型检查的代码来说,这仍然是一个合理的问题,所以让我继续回答这个问题。:)
不受支持的原因是,与许多其他语言不同,类型检查器实际上能够正确地推断泛型,因此我们做出了判断,认为语法会妨碍它,并决定不允许它。事实证明,如果你不担心,我们会推断正确,并且仍然给出有用的类型错误。当然,您可以设计出不像您希望的那样"快速失败"的人为代码,但它确实是人为的。以您的示例的以下修复为例:
<?hh // strict
function main(): void {
$myVector = Vector {}; // I intend this to be a Vector<int>
$myVector[] = 0;
$myVector[] = 'oops'; // Oops! Now it's inferred to be a Vector<mixed>
}
您可能会认为这很糟糕,因为您打算有一个Vector<int>
,但实际上有一个没有类型错误的Vector<mixed>
;您可能希望在创建它时能够表达这一点,以便将'oops'
添加到其中会导致这样的错误。但是没有类型错误只是因为您从未真正尝试使用$myVector
!如果您试图取出它的任何值,或者从函数中返回它,就会得到某种类型兼容性错误。例如:
<?hh // strict
function main(): Vector<int> {
$myVector = Vector {}; // I intend this to be a Vector<int>
$myVector[] = 0;
$myVector[] = 'oops'; // Oops! Now it's inferred to be a Vector<mixed>
return $myVector; // Type error!
}
return
语句将导致类型错误,指出'oops'
是一个字符串,与int
返回类型注释不兼容——这正是您想要的。所以这个推断是正确的,它是有效的,你实际上不需要显式地注释局部变量的类型。
但是如果你真的想的话,为什么不能呢?因为在实例化新对象时只注释泛型并不是真正正确的特性。你所说的"但偶尔我真的想注释Vector<int> {}
"的核心实际上是"但偶尔我真的想注释locals"。因此,正确的语言特性不是让您编写$x = Vector<int> {};
,而是让您显式声明变量并编写Vector<int> $x = Vector {};
——这也允许编写int $x = 42;
之类的东西。向语言中添加显式变量声明比仅仅在对象实例化时注释泛型更通用、更合理。(然而,这并不是一个正在积极开发的功能,我也不认为它会在近期或中期出现,所以现在不要抱太大希望。但是我们做这个决定的原因是要留有余地。
此外,允许这两种语法中的任何一种都会在此时产生误导。泛型仅由静态类型检查器强制执行,并由运行时擦除。这意味着,如果您从PHP或Hack部分模式代码中获得未类型化的值,则运行时不可能检查泛型的实际类型。请注意,未类型化的值是"信任程序员"的,因此您也可以在静态类型检查器中对它们做任何事情,请考虑以下代码,其中包括您提出的假设语法:
<?hh // partial
function get_foo() /* unannotated */ {
return 'not an int';
}
<?hh // strict
function f(): void {
$v = Vector<int> {};
$v[] = 1; // OK
// $v[] = 'whoops'; // Error since explicitly annotated as Vector<int>
// No error from static typechecker since get_foo is unannotated
// No error from runtime since generics are erased
$v[] = get_foo();
}
当然,你不可能在100%严格模式代码中使用没有注释的值,但是我们必须考虑它如何与所有潜在的用法交互,包括部分模式甚至PHP中的无类型代码。