为什么Hack中不允许使用泛型实例化语法?

时间:2014-08-12 22:53:54

标签: static-typing hacklang fail-fast

来自the docs

  

注意:HHVM允许$x = Vector<int>{5,10};之类的语法,但是Hack   在这种情况下不允许语法,而是选择推断   它

有具体原因吗?这不违反the fail-fast rule吗?

在某些情况下,这会导致错误被拒绝,从而导致更难回溯。

例如:

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

1 个答案:

答案 0 :(得分:3)

我在Facebook上使用Hack类型系统和typechecker。这个问题已经在FB内部被问了好几次,并且有一个很好的,外部可见的地方可以写下它的答案。

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

<?hh // strict
function main() : void {
    $myVector = new Vector([]); // no generic syntax
    $myVector->addAll(require 'some_external_source.php');
}

但是,代码does not pass the typechecker由于在{...}}之外使用了顶层,因此在HHVM上实际执行它的任何结果都是未定义的行为,这使得整个讨论对该代码没有帮助。

但是对于其他潜在的代码片段来说,它仍然是一个合法的问题,所以让我继续实际回答它。 :)

它不受支持的原因是因为类型检查器实际上能够正确推断泛型,与许多其他语言不同,所以我们做出了判断调用语法会妨碍它,并决定不允许它。事实证明,如果你只是不担心,我们会推断它,并仍然提供有用的类型错误。你当然可以想出一些人为的代码,这些代码不会以你想要的方式“快速失败”,但实际上是设计的。以你的例子的这个修正为例:

require

你可能会认为这很糟糕,因为你打算有一个<?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 {};之类的内容。向语言添加显式变量声明是一种更为通用,合理的补充,而不仅仅是在对象实例化中注释泛型。 (然而,这不是一个正在积极开展工作的功能,我也不能在近期到中期看到它如此,所以现在不要抱有希望。但是让选项保持开放是我们做出这个决定的原因。)< / p>

此外,允许这些语法中的任何一种在此时都会产生误导。泛型仅由静态类型检查器强制执行,并由运行时擦除。这意味着如果从PHP或Hack部分模式代码中获取无类型值,则运行时无法检查泛型的实际类型。注意到无类型值是“信任程序员”,所以你也可以在静态类型检查器中对它们做任何事情,考虑下面的代码,其中包括你提出的假设语法:

int $x = 42;

当然,您不能在100%严格模式代码中使用未注释的值,但我们必须考虑它与所有潜在用法的交互方式,包括部分模式甚至PHP中的无类型代码。

相关问题