Collections: Hack Arrays

Hack provides additional "array-like" types: dict, vec, and keyset; we expect these to eventually replace the collection objects, and will be recommending they are used instead once the Hack Standard Library leaves 'preview' status.

The Types

  • vec<T> is an indexable and traversable container of items of type T; it is a replacement for Vector, ImmVector, and ConstVector
  • dict<Tk as arraykey, Tv> is an indexable and keyed traversable container of items of type Tv; it is a replacement for Map, ImmMap, and ConstMap. Tk must be an arraykey, i.e. string or int keys.
  • keyset<T as arraykey> is an indexable and keyed traversable container of unique items of type T; it is a replacement for Set, ImmSet, and ConstSet. It can only contain arraykey values, i.e. string or int.

All of these types preserve insertion order.

Creating Hack Arrays

Literals are created with [] syntax:

<?hh

namespace Hack\UserDocumentation\Collections\HackArrays\Examples\Literals;

function main(): void {
  var_dump(vec[1, 2, 3, 1]);
  var_dump(dict['a' => ord('a'), 'b' => ord('b'), 'c' => ord('c')]);
  var_dump(keyset[1, 2, 3, 1]);
}

main();
Output
vec(4) {
  int(1)
  int(2)
  int(3)
  int(1)
}
dict(3) {
  ["a"]=>
  int(97)
  ["b"]=>
  int(98)
  ["c"]=>
  int(99)
}
keyset(3) {
  int(1)
  int(2)
  int(3)
}

Additionally, Hack provides the following conversion functions:

  • vec<Tv>(Traversable<Tv>): vec<Tv>
  • dict<Tk as arraykey, Tv>(KeyedTraversable<Tk, Tv>): dict<Tk, Tv>
  • keyset<Tv as arraykey>(Traversable<Tv>): keyset<Tv>
<?hh

namespace Hack\UserDocumentation\Collections\HackArrays\Examples\Conversions;

function main(): void {
  var_dump(vec(Vector { 1, 2, 3, 1 }));
  var_dump(dict(Map {'a' => ord('a'), 'b' => ord('b'), 'c' => ord('c') }));
  var_dump(keyset(Vector {1, 2, 3, 1 }));
  var_dump(keyset(Set {1, 2, 3, 1 }));
}

main();
Output
vec(4) {
  int(1)
  int(2)
  int(3)
  int(1)
}
dict(3) {
  ["a"]=>
  int(97)
  ["b"]=>
  int(98)
  ["c"]=>
  int(99)
}
keyset(3) {
  int(1)
  int(2)
  int(3)
}
keyset(3) {
  int(1)
  int(2)
  int(3)
}

Library functions

As Hack Arrays are not objects, they do not have methods. Library support is provided via the Hack Standard Library.

A full API reference is automatically generated, however we strongly recommend reading the README first.

Compared to Hack Collections

Hack Collections (Vector, Map, Set, ...) are objects; Hack Arrays (vec, dict, keyset) are values. The primary implications are:

  • Hack Collections are implicitly passed by reference
  • Hack Arrays are implicitly passed by value, with copy-on-write-like behavior
  • Hack Arrays do not have methods and require library functions
  • There is no need for Const or Imm variants of Hack Arrays

The difference between reference and copy-on-write semantics is shown below:

<?hh

namespace Hack\UserDocumentation\Collections\HackArrays\Examples\RefVsCow;

function do_stuff($container) {
  $container[] = 456;
  return $container;
}

function main(): void {
    $a = Vector { 123 };
    $b = do_stuff($a);
    $x = vec[123];
    $y = do_stuff($x);

    var_dump([
        array(
            'in' => $a,
            'out' => $b,
            'mutated' => $a == $b,
        ),
        array(
            'in' => $x,
            'out' => $y,
            'mutated' => $x == $y,
        ),
    ]);
}

main();
Output
array(2) {
  [0]=>
  array(3) {
    ["in"]=>
    object(HH\Vector)#1 (2) {
      [0]=>
      int(123)
      [1]=>
      int(456)
    }
    ["out"]=>
    object(HH\Vector)#1 (2) {
      [0]=>
      int(123)
      [1]=>
      int(456)
    }
    ["mutated"]=>
    bool(true)
  }
  [1]=>
  array(3) {
    ["in"]=>
    vec(1) {
      int(123)
    }
    ["out"]=>
    vec(2) {
      int(123)
      int(456)
    }
    ["mutated"]=>
    bool(false)
  }
}

Compared to PHP Arrays

  • Hack Arrays do not implicitly convert int-like string keys to ints
  • Hack Arrays throw exceptions instead of returning null when undefined indices are accessed
  • Can not contain references

The ban on references is demonstrated below:

<?hh

namespace Hack\UserDocumentation\Collections\HackArrays\Examples\CanNotContainReferences;

function foo(int &$foo) {
  $foo = 346;
}

$y = vec[1,2,3];
foo($y[2]);
var_dump($y);
Output
Fatal error: Uncaught exception 'InvalidArgumentException' with message 'Vecs cannot contain references' in /Users/fredemmott/code/user-documentation/guides/hack/23-collections/11-hack-arrays-examples/can-not-contain-references.php:10
Stack trace:
#0 {main}