Examples ¶
Here are some more examples that showcase much of the
functionality of the Hack collection interfaces and classes.
Collections are objects ¶
<?hh
function main() {
// Collections are objects.
$v = Vector {1, 2, 3};
echo is_array($v) ? '$v is an array' : '$v is not an array';
echo "\n";
echo is_object($v) ? '$v is an object' : '$v is not an object';
echo "\n";
var_dump($v);
}
main();
function main() {
// Collections are objects.
$v = Vector {1, 2, 3};
echo is_array($v) ? '$v is an array' : '$v is not an array';
echo "\n";
echo is_object($v) ? '$v is an object' : '$v is not an object';
echo "\n";
var_dump($v);
}
main();
The above example will output:
$v is not an array
$v is an object
object(Vector)#1 (3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
Reference-like Semantics ¶
<?hh
// Like all objects, collections has "reference-like"
// semantics for assignment, parameter passing, and
// foreach.
function foo($v) {
$v[1] = 7;
}
function main() {
$v1 = Vector {1, 2, 3};
$v2 = $v1;
foo($v2);
var_dump($v1);
echo "\n";
foreach ($v1 as $key => $value) {
echo $key . " => " . $value . "\n";
if ($key == 0) {
$v1[2] = 9;
}
}
}
main();
// Like all objects, collections has "reference-like"
// semantics for assignment, parameter passing, and
// foreach.
function foo($v) {
$v[1] = 7;
}
function main() {
$v1 = Vector {1, 2, 3};
$v2 = $v1;
foo($v2);
var_dump($v1);
echo "\n";
foreach ($v1 as $key => $value) {
echo $key . " => " . $value . "\n";
if ($key == 0) {
$v1[2] = 9;
}
}
}
main();
The above example will output:
object(Vector)#1 (3) {
[0]=>
int(1)
[1]=>
int(7)
[2]=>
int(3)
}
0 => 1
1 => 7
2 => 9
Iterator Invalidation ¶
<?hh
// Certain kinds of modification, such as
// removing an element, will cause iterators to
// be invalidated (including foreach loops).
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
foreach ($m as $key => $value) {
echo $key . " => " . $value . "\n";
if ($key == 'a') {
$m->remove('d');
}
}
}
main();
// Certain kinds of modification, such as
// removing an element, will cause iterators to
// be invalidated (including foreach loops).
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
foreach ($m as $key => $value) {
echo $key . " => " . $value . "\n";
if ($key == 'a') {
$m->remove('d');
}
}
}
main();
The above example will output:
a => 1
HipHop Fatal error:
Uncaught exception 'InvalidOperationException' with message 'Collection was
modified during iteration'
Key Not Present ¶
<?hh
// Using "$c[$k]" syntax or using the at() method
// will throw an exception if the specified key is
// not present. Using the get() method will return
// NULL if the specified key is not present.
function main() {
$m = Vector {};
try {
var_dump($m[0]);
} catch (OutOfBoundsException $e) {
echo "Caught exception 1\n";
}
try {
var_dump($m->at(0));
} catch (OutOfBoundsException $e) {
echo "Caught exception 2\n";
}
try {
var_dump($m->get(0));
} catch (OutOfBoundsException $e) {
echo "Caught exception 3\n";
}
}
main();
// Using "$c[$k]" syntax or using the at() method
// will throw an exception if the specified key is
// not present. Using the get() method will return
// NULL if the specified key is not present.
function main() {
$m = Vector {};
try {
var_dump($m[0]);
} catch (OutOfBoundsException $e) {
echo "Caught exception 1\n";
}
try {
var_dump($m->at(0));
} catch (OutOfBoundsException $e) {
echo "Caught exception 2\n";
}
try {
var_dump($m->get(0));
} catch (OutOfBoundsException $e) {
echo "Caught exception 3\n";
}
}
main();
The above example will output:
Caught exception 1
Caught exception 2
NULL
Const Interfaces ¶
<?hh
// The "read-only" style interfaces (such as ConstMap)
// can be used to indicate that a function will not
// modify the collection.
function foo(ConstMap<string,int> $m, string $k): int {
echo $m[$k] . "\n";
}
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
foo($m, 'c');
}
// The "read-only" style interfaces (such as ConstMap)
// can be used to indicate that a function will not
// modify the collection.
function foo(ConstMap<string,int> $m, string $k): int {
echo $m[$k] . "\n";
}
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
foo($m, 'c');
}
The above example will output:
3
keys() method ¶
<?hh
// The keys() method can be used on Vector and Map
// to get a Vector of the keys.
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
$v = $m->keys();
var_dump($v);
}
main();
// The keys() method can be used on Vector and Map
// to get a Vector of the keys.
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
$v = $m->keys();
var_dump($v);
}
main();
The above example will output:
object(Vector)#2 (4) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
[3]=>
string(1) "d"
}
map() method ¶
<?hh
// The map() method can be used on any collection to
// get a concrete collection (usually the same type as
// the original) containing each value with some
// operation applied.
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
$m2 = $m->map(function(int $x):int { return $x + 10; });
var_dump($m2);
}
main();
// The map() method can be used on any collection to
// get a concrete collection (usually the same type as
// the original) containing each value with some
// operation applied.
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
$m2 = $m->map(function(int $x):int { return $x + 10; });
var_dump($m2);
}
main();
The above example will output:
object(Map)#2 (4) {
["a"]=>
int(11)
["b"]=>
int(12)
["c"]=>
int(13)
["d"]=>
int(14)
}
filter() method ¶
// The filter() method can be used on any collection to
// get a concrete collection (usually the same type as
// the original) containing the values that meet some
// condition.
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
$m2 = $m->filter(function(int $x):bool { return $x % 2 == 0; });
var_dump($m2);
}
main();
// get a concrete collection (usually the same type as
// the original) containing the values that meet some
// condition.
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
$m2 = $m->filter(function(int $x):bool { return $x % 2 == 0; });
var_dump($m2);
}
main();
The above example will output:
object(Map)#2 (2) {
["b"]=>
int(2)
["d"]=>
int(4)
}
Iterables ¶
<?hh
// All Iterables support map() and filter() to support
// chaining.
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
$result = $m->filter(function(int $x):bool { return $x % 2 == 0; })
->map(function(int $x):int { return $x + 1; });
foreach ($result as $key => $value) {
echo $key . " => " . $value . "\n";
}
}
main();
// All Iterables support map() and filter() to support
// chaining.
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
$result = $m->filter(function(int $x):bool { return $x % 2 == 0; })
->map(function(int $x):int { return $x + 1; });
foreach ($result as $key => $value) {
echo $key . " => " . $value . "\n";
}
}
main();
The above example will output:
b => 3
d => 5
Lazy Views ¶
<?hh
// The Iterable objects returned by view() and
// items() are 'lazy' views of the original
// collection; this means that if a value in the
// underlying collection is changed, the lazy view
// will reflect this change. Also, certain kinds
// of modifications that invalidate iterators
// (such as removing an element) will also
// invalidate lazy views.
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
$iterable = $m->items();
$m['a'] = 100;
$i = 0;
foreach ($iterable as $t) {
echo $t[0] . " => " . $t[1] . "\n";
if ($i == 2) {
echo "Removing key 'a'\n";
$m->remove('a');
}
++$i;
}
}
main();
// The Iterable objects returned by view() and
// items() are 'lazy' views of the original
// collection; this means that if a value in the
// underlying collection is changed, the lazy view
// will reflect this change. Also, certain kinds
// of modifications that invalidate iterators
// (such as removing an element) will also
// invalidate lazy views.
function main() {
$m = Map {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4};
$iterable = $m->items();
$m['a'] = 100;
$i = 0;
foreach ($iterable as $t) {
echo $t[0] . " => " . $t[1] . "\n";
if ($i == 2) {
echo "Removing key 'a'\n";
$m->remove('a');
}
++$i;
}
}
main();
The above example will output:
a => 100
b => 2
c => 3
Removing key 'a'
HipHop Fatal error:
Uncaught exception 'InvalidOperationException' with message 'Collection was
modified during iteration'
Vector ¶
<?hh
function main() {
$vec = Vector {11};
$vec->addAll(Vector {22, 33, 44});
var_dump($vec);
}
main();
function main() {
$vec = Vector {11};
$vec->addAll(Vector {22, 33, 44});
var_dump($vec);
}
main();
The above example will output:
object(Vector)#1 (4) {
[0]=>
int(11)
[1]=>
int(22)
[2]=>
int(33)
[3]=>
int(44)
}
Map ¶
function main() {
$map = Map {'a' => 11};
$map->setAll(Map {'b' => 22, 'c' => 33});
var_dump($map);
}
main();
$map = Map {'a' => 11};
$map->setAll(Map {'b' => 22, 'c' => 33});
var_dump($map);
}
main();
The above example will output:
object(Map)#1 (3) {
["a"]=>
int(11)
["b"]=>
int(22)
["c"]=>
int(33)
}
Building a Concrete Collection ¶
<?hh
// A concrete collection can be built from an
// Iterable or KeyedIterable by passing it to a
// collection constructor.
function main() {
$m = Map {'a' => 11, 'b' => 22, 'c' => 33};
$v = new Vector($m);
var_dump($v);
}
main();
// A concrete collection can be built from an
// Iterable or KeyedIterable by passing it to a
// collection constructor.
function main() {
$m = Map {'a' => 11, 'b' => 22, 'c' => 33};
$v = new Vector($m);
var_dump($v);
}
main();
The above example will output:
object(Vector)#1 (3) {
[0]=>
int(11)
[1]=>
int(22)
[2]=>
int(33)
}
General Collection Interfaces ¶
<?hh
// Example showing add(), items(), and the general collection interfaces
function process_elements<T>(ConstCollection<T> $in): OutputCollection<T> {
$out = Vector {};
if (!($in instanceof ConstVector)) {
return null;
}
foreach ($in->items() as $elm) {
if ($elm > 1) {
$out->add($elm);
}
}
return $out;
}
function main(): void {
$x = Vector {1, 2, 3};
var_dump(process_elements($x));
}
main();
// Example showing add(), items(), and the general collection interfaces
function process_elements<T>(ConstCollection<T> $in): OutputCollection<T> {
$out = Vector {};
if (!($in instanceof ConstVector)) {
return null;
}
foreach ($in->items() as $elm) {
if ($elm > 1) {
$out->add($elm);
}
}
return $out;
}
function main(): void {
$x = Vector {1, 2, 3};
var_dump(process_elements($x));
}
main();
The above example will output:
object(HH\Vector)#2 (2)
{
[0]=>
int(2)
[1]=>
int(3)
}
Generators ¶
<?hh
// Generators implement the Iterator interface;
// a generator can be passed anywhere where an
// Iterator is expected.
function foo(Iterator<int> $it) { .. }
function g() { yield 1; yield 2; }
function main() {
$gen = g();
foo($gen);
}
main();
// Generators implement the Iterator interface;
// a generator can be passed anywhere where an
// Iterator is expected.
function foo(Iterator<int> $it) { .. }
function g() { yield 1; yield 2; }
function main() {
$gen = g();
foo($gen);
}
main();
Real-world, existing
code
<?hh
// This function does a relational join on two mappish style arrays into one
// mappish style array
function array_compose($f, $g): array {
$ret = array();
foreach ($f as $x => $y) {
if (array_key_exists($y, $g)) {
$ret[$x] = $g[$y];
}
}
return $ret;
}
// This function does a relational join on two mappish style arrays into one
// mappish style array
function array_compose($f, $g): array {
$ret = array();
foreach ($f as $x => $y) {
if (array_key_exists($y, $g)) {
$ret[$x] = $g[$y];
}
}
return $ret;
}
Possible Indexish modification #1
<?hh
// Use Indexish to be able to pass Map or Vector, in addition
// to arrays, to this function and return an array like before. Notice how
// foreach and bracket syntax is being used.
function array_compose<T1, T2, T3>(
Indexish<T1, T2> $f,
Indexish<T2, T3> $g
): array<T1, T3> {
$ret = array();
foreach ($f as $x => $y) {
if (array_key_exists($y, $g)) {
$ret[$x] = $g[$y];
}
}
return $ret;
}
// Use Indexish to be able to pass Map or Vector, in addition
// to arrays, to this function and return an array like before. Notice how
// foreach and bracket syntax is being used.
function array_compose<T1, T2, T3>(
Indexish<T1, T2> $f,
Indexish<T2, T3> $g
): array<T1, T3> {
$ret = array();
foreach ($f as $x => $y) {
if (array_key_exists($y, $g)) {
$ret[$x] = $g[$y];
}
}
return $ret;
}
Possible Indexish modification #2
<?hh
// Use Indexish to be able to pass Map or Vector in addition
// to arrays, to this function and return a Map, which is what this
// function is actually returning in practice. Notice how
// foreach and bracket syntax is being used.
function map_compose<T1, T2, T3>(
Indexish<T1, T2> $f,
Indexish<T2, T3> $g
): Map<T1, T3> {
$ret = Map {};
foreach ($f as $x => $y) {
if (array_key_exists($y, $g)) {
$ret[$x] = $g[$y];
}
}
return $ret;
}
// Use Indexish to be able to pass Map or Vector in addition
// to arrays, to this function and return a Map, which is what this
// function is actually returning in practice. Notice how
// foreach and bracket syntax is being used.
function map_compose<T1, T2, T3>(
Indexish<T1, T2> $f,
Indexish<T2, T3> $g
): Map<T1, T3> {
$ret = Map {};
foreach ($f as $x => $y) {
if (array_key_exists($y, $g)) {
$ret[$x] = $g[$y];
}
}
return $ret;
}
Test Code
<?hh
// Copyright 2004-present Facebook. All Rights Reserved.
function main_indexish(): void {
$arr1 = array(1, 2, 3, 4, 5);
$arr2 = array(6, 7, 8, 9, 10);
var_dump(array_compose($arr1, $arr2)); // original
var_dump(array_compose($arr1, $arr2)); // modified
var_dump(map_compose($arr1, $arr2));
$map1 = Map {0 => 1, 1 => 2, 2 => 3, 3 => 4, 4 => 5};
$map2 = Map {0 => 6, 1 => 7, 2 => 8, 3 => 9, 4 => 10};
var_dump(array_compose($map1, $map2)); // original
var_dump(array_compose($map1, $map2)); // modified
var_dump(map_compose($map1, $map2));
}
main_indexish()
// Copyright 2004-present Facebook. All Rights Reserved.
function main_indexish(): void {
$arr1 = array(1, 2, 3, 4, 5);
$arr2 = array(6, 7, 8, 9, 10);
var_dump(array_compose($arr1, $arr2)); // original
var_dump(array_compose($arr1, $arr2)); // modified
var_dump(map_compose($arr1, $arr2));
$map1 = Map {0 => 1, 1 => 2, 2 => 3, 3 => 4, 4 => 5};
$map2 = Map {0 => 6, 1 => 7, 2 => 8, 3 => 9, 4 => 10};
var_dump(array_compose($map1, $map2)); // original
var_dump(array_compose($map1, $map2)); // modified
var_dump(map_compose($map1, $map2));
}
main_indexish()
The above example will output:
array(4) {
[0]=>
int(7)
[1]=>
int(8)
[2]=>
int(9)
[3]=>
int(10)
}
array(4) {
[0]=>
int(7)
[1]=>
int(8)
[2]=>
int(9)
[3]=>
int(10)
}
object(Map)#1 (4) {
[0]=>
int(7)
[1]=>
int(8)
[2]=>
int(9)
[3]=>
int(10)
}
array(4) {
[0]=>
int(7)
[1]=>
int(8)
[2]=>
int(9)
[3]=>
int(10)
}
array(4) {
[0]=>
int(7)
[1]=>
int(8)
[2]=>
int(9)
[3]=>
int(10)
}
object(Map)#3 (4) {
[0]=>
int(7)
[1]=>
int(8)
[2]=>
int(9)
[3]=>
int(10)
}
Limitations ¶
1. Collections do not support taking elements by
reference for performance and type safety reasons.
2. Collections do not support dynamic properties.
3. <, <=, >, and >= are not currently
supported for collections (these will raise an error at runtime), though these
will likely be supported in the future.
Future work on collections ¶
For this first version of Hack collections, there are features
that did not make the cut.
1. Better support for iterators:
o Bidirectional iterators (ability to move both
forward and backward)
o Ability to quickly get an iterator pointing to
the last element
o find() method for quickly getting an iterator for a
given key (Map) or for a given value (Set)
o findKey() method for quickly getting an iterator for a
given index (Vector)
o Swapping items via iterators
o Comparing two iterators for equality (i.e.,
check if they point to the same element in the same collection)
2. Supporting object keys for Map, and supporting object values for Set.
3. Methods for finding or removing elements by
value in O(n) time.
4. Generic support for erasing multiple items
cleanly and efficiently (ex. an idiom for erasing elements during
iteration)
5. Other possible data structure types such as LinkedList, SortedMap, MultiSet, MultiMap, etc..
6. insert() method for quickly inserting an element at an
arbitrary position in an order-preserving collection (this would be useful if
we later support LinkedList).
No comments:
Post a Comment