hack泛型:子類型

2018-11-09 15:40 更新

與亞型相關(guān)的一個(gè)看似與直覺相反的領(lǐng)域是泛型。

<?hh

namespace Hack\UserDocumentation\Generics\Subtypes\Examples\Intuitive;

function echo_add(num $x, num $y): void {
  echo $x + $y;
}

function get_int(): int {
  return rand();
}

function run(): void {
  $num1 = get_int();
  $num2 = get_int();
  echo_add($num1, $num2); // int is a subtype of num
}

run();

Output

2229204202

由于int是一個(gè)子類型num,類型轉(zhuǎn)換器通過傳遞int一個(gè)函數(shù)是完全正確的num。如果你傳遞一個(gè)float函數(shù)就沒問題了。如果你傳遞一個(gè)int和一個(gè)float函數(shù),也沒關(guān)系。

但是,你認(rèn)為類型分析者應(yīng)該接受嗎?

<?hh

namespace Hack\UserDocumentation\Generics\Subtypes\Examples\CounterIntuitive;

class Box<T> {
  private Vector $box;
  public function __construct(int $firstItem) {
    $this->box = Vector {$firstItem};
  }
  public function add(T $v) {
    $this->box[] = $v;
  }
}

function addRandomToBox(Box<num> $x): void {
  $x->add(rand());
}

function createBox(): Box<int> {
  return new Box(3);
}

function run(): void {
  $box = createBox(); // we have a Box<int>
  addRandomToBox($box); // typechecker cannot guarantee a Box<int> now.
  var_dump($box); // HHVM doesn't care since we erase generics anyway.
}

run();

Output

object(Hack\UserDocumentation\Generics\Subtypes\Examples\CounterIntuitive\Box)#2 (1) {
  ["box":"Hack\UserDocumentation\Generics\Subtypes\Examples\CounterIntuitive\Box":private]=>
  object(HH\Vector)#1 (2) {
    [0]=>
    int(3)
    [1]=>
    int(123434323)
  }
}

這似乎是應(yīng)該是有效的傳遞Box<int>一個(gè)函數(shù)期望一個(gè)Box<num>自從int是一個(gè)子類型num。然而,在泛型的情況下,子類型關(guān)系與其原始類型的對(duì)應(yīng)關(guān)系并不一致。

原因是因?yàn)槟愕姆盒蛯?duì)象是通過引用傳遞的,所以類型檢查者沒有辦法安全地知道你是否正在修改Boxin addRandomToBox()以包含不是的東西num。雖然明顯地向我們說,我們正在添加int中addRandomToBox(),該typechecker實(shí)際上不考慮發(fā)生了什么。所以不確定的是,返回給我們的還是一個(gè)Box<int>。

HHVM運(yùn)行時(shí)并不在意,因?yàn)槲覀冊(cè)谶\(yùn)行時(shí)會(huì)刪除泛型。

不變的集合和數(shù)組

由于不可變的集合不能被改變array(即拷貝而不是引用),泛型與上面討論的子類型關(guān)系實(shí)際上會(huì)通過類型檢查器。這是因?yàn)轭愋蜋z查器可以保證實(shí)體在返回給調(diào)用者時(shí)不會(huì)被改變。

<?hh

namespace Hack\UserDocumentation\Generics\Subtypes\Examples\Immutable;

function addRandomToArray(array<num> $x): void {
  $x[] = 3.2; // this is a copy, not a reference
}

function createArray(): array<int> {
  return array(3);
}

function run(): void {
  $arr = createArray(); // we have a array<int>
  // typechecker CAN guarantee array<int> now since what is received by
  // addRandomToArray() is a copy (passed-by-value)
  addRandomToArray($arr);
  var_dump($arr); // Still only going to contain 3, not the 3.2.
}

run();

Output

array(1) {
  [0]=>
  int(3)
}
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)