1

文档

注意: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。但是必须将错误追溯到实际导入有缺陷的数据的那一点。如果可以首先使用通用语法来实例化向量,那么这将不是必需的:

$myVector = new Vector<int>([]);
$myVector->addAll(require 'some_external_source.php'); // fail immediately
4

1 回答 1

3

我在 Facebook 从事 Hack 类型系统和类型检查器的工作。这个问题已经在 FB 内部被问过几次,最好有一个很好的、外部可见的地方来写下这个问题的答案。

因此,首先,您的问题是以以下代码为前提的:

<?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> {}”的核心实际上是“但偶尔我真的想注释本地人”。所以正确的语言特性不是让你写$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 中的无类型代码。

于 2014-08-13T17:54:12.243 回答