Collections: Typing

You will generally instantiate Hack collection objects through the concrete classes. However, what parameter type is accepted by or returned from a method could indeed be one of the interfaces.

The following example shows how you can pass any of the concrete classes to a function that takes an Iterable<T>. In these cases T is representing the types of the values of each collection. So int for $v and string for $m. For the maps, the types of the keys don't matter in this case.

<?hh

namespace Hack\UserDocumentation\Collections\Typing\Examples\Iter;

// Given that this function takes an Iterable, you can pass any of
// our current concerte collection classes to this method
function take_iterable<T>(Iterable<T> $it): array<T> {
  return $it->toValuesArray();
}

function run(): void {
  $v = Vector {100, 200, 300};
  var_dump(take_iterable($v));
  $iv = ImmVector {400, 500, 600};
  var_dump(take_iterable($iv));
  $s = Set {'A', 'B'};
  var_dump(take_iterable($s));
  $is = ImmSet {'C', 'D'};
  var_dump(take_iterable($is));
  $m = Map {'A' => 'Z', 'B' => 'Y'};
  var_dump(take_iterable($m));
  $im = ImmMap {'C' => 'X', 'D' => 'W'};
  var_dump(take_iterable($im));
}

run();
Output
array(3) {
  [0]=>
  int(100)
  [1]=>
  int(200)
  [2]=>
  int(300)
}
array(3) {
  [0]=>
  int(400)
  [1]=>
  int(500)
  [2]=>
  int(600)
}
array(2) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
}
array(2) {
  [0]=>
  string(1) "C"
  [1]=>
  string(1) "D"
}
array(2) {
  [0]=>
  string(1) "Z"
  [1]=>
  string(1) "Y"
}
array(2) {
  [0]=>
  string(1) "X"
  [1]=>
  string(1) "W"
}

The following example shows how you can use ConstVector to ensure that you are passing a vector that cannot be modified. You might ask why not annotate it with ImmVector? That's because you can imagine other immutable vectors being implemented in the future; and since all immutable vectors must implement ConstVector, it is more expressive to use ConstVector instead. The same rationale is applied to the return type MutableVector.

Now, if you know the function will only take an ImmVector for now and always, then type annotating it as ImmVector would be fine.

<?hh

namespace Hack\UserDocumentation\Collections\Typing\Examples\CV;

// Given that this function takes a ConstVector, you can pass any concrete
// collection that implements the interface (currently Vector and ImmVector)
function filter_vec(\ConstVector<int> $cv): \MutableVector<int> {
  try {
    $cv[] = 900; // cannot modify a ConstVector
  } catch (\InvalidOperationException $ex) {
    var_dump($ex->getMessage());
  }
  $mv = $cv->filter($x ==> $x % 4 === 0);
  return $mv->toVector();
}

function run(): void {
  $iv = ImmVector {100, 125, 150, 175};
  var_dump(filter_vec($iv));
}

run();
Output
string(51) "Cannot modify immutable object of type HH\ImmVector"
object(HH\Vector)#5 (1) {
  [0]=>
  int(100)
}