Pages

Wednesday, March 26, 2014

Tuples



Table of Contents 

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'234'hi');
    
$tup tuple('3'234'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'234'hi');
    
$tup tuple('3'234'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(1357);
  
$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((stringstring$tup): Vector<(stringstring)> {
  
$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(): (stringint) {
    
$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 {
  public 
$a tuple(5,6);
  public static 
$b tuple (78);
}







No comments:

Post a Comment