Table of Contents ¶
- Use Cases
- Tuples Are Implemented As Arrays
- Type Annotating With Tuples
- Returning a Tuple From a Method
- Initializer Expressions
Hack has a basic implementation for tuples. A tuple is, for all intents and purposes, an immutable array. With a PHP array, elements can be added and removed at will. With a tuple, after initialization, elements cannot be added to or removed from the tuple. In other words, it remains a fixed size. It is important to note that each element within the tuple is mutable and can be changed.
Note:The values inside a tuple are, of course, mutable. It is the "shape" of a tuple that is not mutable ... no removing, adding or changing the types of elements
Also, while collections and type-annotated arrays are designed to contain elements of one type, tuples allow for the mixing of the type of the elements.
Here is an example of using a tuple:
<?hh
class TupleTest {
public function test(): void {
$arr = array('3', 2, 3, 4, 'hi');
$tup = tuple('3', 2, 3, 4, 'hi');
var_dump($arr);
var_dump($tup);
// Change the 5th element of both. Perfectly fine.
$arr[4] = 'bye';
$tup[4] = 'bye';
var_dump($arr);
var_dump($tup);
// Add a 6th element of both. Not fine. Type checker balks with tuple.
$arr[5] = 'Good!';
$tup[5] = 'Whoops!';
var_dump($arr);
var_dump($tup);
}
}
function main_tup() {
$tp = new TupleTest();
$tp->test();
}
main_tup();
main_tup();
In fact, the Hack type checker will give you the something similar to the following error when trying to insert a new element into a tuple.
The above example will output:
[~/www] hh_client File "TupleTest.php", line 17, characters 10-10: Invalid index
Note:Underneath the covers, a Hack tuple is just an array. There is nothing to stop the above code from running in HHVM. The code will run without issue. Expected behavior, on the other hand, is another issue. It is the type checker that will catch the errors before runtime. Choosing to ignore the type checker is allowed, but fraught with peril and not recommended.
Use Cases ¶
Why use a tuple? There are common use cases for when a tuple can be quite valuable. It is important to note, however, there can be other paradigms to accomplish what a tuple can accomplish in many cases. In addition, noting that they actually mutable at runtime, tuples in Hack should be used when wanting the comfort of an immutable type safety check by the Hack type checker. Because, as of now, tuples at runtime are not necessarily tuples as one would expect them to behave.
- Canonically, a tuple is used when a pair is needed. Hack provides a Pair type that is probably better suited for pairs. Pairs are immutable collections with exactly two elements. Tuple can have a variable number of elements.
- Grouping common values together, that are not necessarily of the same type.
- To return multiple values from a method.
- Thread safety. A known immutable object is inherently morethread-safe than a mutable object. (Although, given that tuples in Hack are, in fact, mutable at runtime, the thread safety use case is not as valuable.)
Tuples Are Implemented As Arrays ¶
The discussion of tuples started with the premise that tuples are immutable. Well, that is true as far as Hack is concerned. In HHVM, tuples are arrays under the covers; thus, HHVM allows tuples to be mutated. In fact, tuples are currently a pure PHP implementation.
<?hh
function tuple(...) {
return func_get_args();
}
func_get_args() returns an array comprising the a function's argument list.
Repeating the code example from the introduction:
<?hh
class TupleTest {
public function test(): void {
$arr = array('3', 2, 3, 4, 'hi');
$tup = tuple('3', 2, 3, 4, 'hi');
var_dump($arr);
var_dump($tup);
// Change the 5th element of both. Perfectly fine.
$arr[4] = 'bye';
$tup[4] = 'bye';
var_dump($arr);
var_dump($tup);
// Add a 6th element of both. Not fine. Type checker balks with tuple.
$arr[5] = 'Good!';
$tup[5] = 'Whoops!';
var_dump($arr);
var_dump($tup);
}
}
function main_tup() {
$tp = new TupleTest();
$tp->test();
}
main_tup();
main_tup();
The above example will output:
array(5) { [0]=> string(1) "3" [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> string(2) "hi" } array(5) { [0]=> string(1) "3" [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> string(2) "hi" } array(5) { [0]=> string(1) "3" [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> string(3) "bye" } array(5) { [0]=> string(1) "3" [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> string(3) "bye" } array(6) { [0]=> string(1) "3" [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> string(3) "bye" [5]=> string(5) "Good!" } array(6) { [0]=> string(1) "3" [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> string(3) "bye" [5]=> string(7) "Whoops!" }
Declaring a tuple() with five elements really calls func_get_args() where each of those five elements are part of an integer-indexed array. That is why tuples are indexed exactly like arrays. And arrays are mutable in every way in PHP.
Type Annotating With Tuples ¶
To review, here is how a tuple is created and used:
<?hhfunction test_tup() {
$tup = tuple(1, 3, 5, 7);
$tup[2] = 6;
$tup[4] = 9; // Hack type error since cannot add to tuple}
However, when adding type annotations that require a tuple, the tuple reserved word is not used.
<?hh// test_tup takes a two-string tuple and returns a Vector of two-string tuples.function test_tup((string, string) $tup): Vector<(string, string)> {
$vec = Vector{$tup};
return $vec;
}
Returning a Tuple From a Method ¶
There may be times when it is necessary to multiple values from a method. There are a few options available to accomplish this task. Arrays can be used. Some sort of collection type (e.g. Vector) could be used. And, of course, tuples could be used. Here is code that makes use of some of the options:
<?hh
class ReturnMultipleValues {
public function foo(): array<mixed> {
$arr = array("Hello", 3);
$arr[2] = 4;
var_dump($arr);
return $arr;
}
public function bar(): Vector<mixed> {
$vec = Vector {"Hello", 3};
$vec->add(4);
var_dump($vec);
return $vec;
}
// This is how a tuple is returned from a method
public function baz(): (string, int) {
$tup = tuple("Hello", 3);
//$tup[2] = 4;
return $tup;
}
}
function main_tup() {
$rmv = new ReturnMultipleValues();
$rmv->foo();
$rmv->bar();
$rmv->baz();
}
main_tup();
So, why use a tuple when one could use an array()or Vector? The answer is mutability. Tuples by their very nature are immutable. No adding elements. No removing elements. No changing the types of values within the tuple (the values themselves can be changed, as long as they are type compatible). And having an immutable-style return type may be exactly was is specified. As discussed tuples are currently mutable at runtime. However, the Hack type checker will catch the mutation of tuples before runtime. If the code $tup[2] = 4; is uncommented, Hack will give the following error:
The above example will output:
File "return_mult_values.php", line 28, characters 10-10: Invalid index
Initializer Expressions ¶
Tuples can be used in initializer expressions. For example, in the below example, class C has two tuple properties being initialized, one static and one instance:
<?hhclass C {
public $a = tuple(5,6);
public static $b = tuple (7, 8);
}
No comments:
Post a Comment