Hack

A Programming Language for HHVM That Interoperates Seamlessly With PHP

von Matthias Gutjahr / @mattsches

Integration mit PHP

Hack Modes

Erlaubt die Vermischung von Hack und anderem (PHP-)Code mit dynamischem Type-Checking.
  • <?hh //strict
    Der gesamte Code muss mit korrekten Type Annotations versehen sein. Außerdem kann kein "Nicht-Hack"-Code aufgerufen werden. Und: keine Arrays!
  • <?hh //partial
    Nicht-annotierte Methoden/Variablen werden vom Type-Checker ignoriert.
  • <?hh //decl
    Eine Mischform; erlaubt gleichzeitig Type-Checking und den Aufruf von z.B. Legacy Code.
  • // UNSAFE
    Deaktiviert den Type-Checker von der Annotation bis zum Ende des Code-Blocks.

Type Annotations

                        
<?php

class MyClass {
  const MyConst = 0;

  private $x;

  public function increment($x) {
    $y = $x + 1;
    return $y;
  }
}
                        
                    
                        
<?hh

class MyClass {
  const int MyConst = 0;

  private string $x = '';

  public function increment(int $x): int {
    $y = $x + 1;
    return $y;
  }
}
                        
                    

Type Annotations 2

                            
# Array-as-vector
array<string> $arrs = array("hi", "bye");

# Array-as-map
array<int, string> $arrs = array(42 => "answer");

# Nullable
protected ?int $ni = null;

# Closure
function foo((function(int, int): string) $x)
                            
                        

Collections

  • Vector / ImmVector
  • Map / ImmMap
  • Set / ImmSet
  • Pair

Vectors

A Vector is an integer-indexed (zero-based) collection with similar semantics to a C++ vector or a C#/Java ArrayList.

                            
<?hh

$vector = Vector {5, 10, 15}; // <-- literal syntax
$vector[] = 20; // <-- square bracket syntax
$vector[] = 25;
$vector->set(5, 30); // <-- explicit set method syntax
$vector->add(35);
echo $vector[0]; // 5
echo $vector->get(1); // 10
$vector->removeKey(2);
$vector->shuffle();
$vector->filter(function($x) {
    return $x >= 20;
});
// usw...
                            
                        

Maps

A Map is an ordered dictionary-style collection. Elements are stored as key/value pairs.

                            
<?hh

$map = Map {"A" => 1, "B" => 2, "C" => 3};
$map["D"] = 4;
$map["E"] = 5;
echo $map["A"]; // 1
echo $map->get("B"); // 2
echo $map->contains("A"); // true
// usw.
                            
                        

Sets

A Set is an ordered collection that stores unique values. Unlike vectors and maps, sets do not have keys, and thus cannot be iterated on keys.

                            
<?hh

$set = Set {"A", "B"};
$set[] = "C";
$set[] = "B";
$set->add("D")->add("E");

// Diff example:
$s = Set {2, 3, 4, 6};
$v = Set {2, 3, 5};
$z = $s->RemoveAll($v); //difference between $v and $s
var_dump($s);

// object(Set)#1 (2) {
//   int(4)
//   int(6)
// }
                            
                        

Pairs

A Pair is an indexed container restricted to containing exactly two elements. Pair has integer keys; key 0 refers to the first element and key 1 refers to the second element.

                            
<?hh

function main() {
  $p = Pair {7, 'a'};
  echo $p[0] . "\n";
  echo $p[1] . "\n";
  foreach ($p as $val) {
    echo $val . "\n";
  }
}
main();

// 7
// a
// 7
// a
                            
                        

Shapes

Since PHP does not have the concept of a structs or records, arrays are many times used to mimic a struct or record-like entity. Arrays are also used as "argument bags" to hold a bunch of arguments that will be passed to a function or method. Shapes were created to bring some structure and type-checking sanity to this use case.


<?hh

type MyShape = shape('id1' => <type>, 'id2' => <type>);
function foo(MyShape $x): void {}
                    

Tuples

A tuple is, for all intents and purposes, an immutable array.

Tuples in Hack should be used when wanting the comfort of an immutable type safety check by the Hack type checker.


<?hh

$tup = tuple(1, 3, 5, 7);
$tup[2] = 6;
$tup[4] = 9; // Hack type error since cannot add to tuple
                    

Async

We are currently finalizing other foundations (e.g. async database, scheduling, and memory handling APIs) which will be required to reach the full potential of async in production.


<?hh

// Ex. 1
async function cached_result<T>(T $x): Awaitable<T> { return $x; }

// Ex. 2
class Foo {}
class Bar {
  public function getFoo(): Foo {
    return new Foo();
  }
}
async function getFoo(int $a): Awaitable<?Foo> {
  if ($a === 0) { return null; }
  $bar = await getBar($a);
  if ($bar !== null) {
    return $bar->getFoo();
  }
  return null;
}
async function getBar(int $a): Awaitable<?Bar> {
  if ($a === 0) { return null; }
  return new Bar();
}
getFoo(4);
                    

Continuations

Continuations provide an easy way to implement iterators without the complexity of implementing Iteratorinterface. Continuations are used in generator functions.


<?hh

function yieldInfiniteInts(): Continuation<int> {
  $i = 0;
  while (true) {
    yield $i++;
  }
}

$generator = yieldInfiniteInts();
foreach ($generator as $value) {
  echo "$value\n";
}
                    

Lambdas

  • PHP Closures benötigen use(), um Variablen aus der umgebenden Methode aufzurufen.
  • PHP Closures sind relativ verbose.
  • Hack Lambdas unterstützen noch keine Type Checks/Type Hints.
  • Hack Lambdas unterstützen noch kein return by reference.
  • Hack Lambdas übernehmen noch kein await als Parameter.
                        
<?php

function foo() {
  $x = 'bar';
  return function ($y) use ($x) {
    return $x . $y;
  };
}
$fn = foo();
echo $fn('baz'); // Outputs barbaz
                        
                    
                        
<?hh

function foo(): (function(string): string){
  $x = 'bar';
  return $y ==> $x . $y;
}
$fn = foo();
echo $fn('baz'); // Outputs barbaz
                        
                    
                        
<?hh

function foo(): (function(string): string) {
  $x = 'bar';
  return ($y) ==> { return $x . $y; };
}
$fn = foo();
echo $fn('baz'); // Outputs barbaz
                        
                    

Weitere Features / Probleme

Constructor Argument Promotion


<?hh
class Person {
  public function __construct(private string $name, private int $age) {}
}
                        

Klassen-Attribute werden automatisch erstellt und die übergebenen Werte zugewiesen.

Weitere Features / Probleme

Variable Anzahl von Argumenten


function var_foo(int $x, ...) : int {
    $arg_arr = func_get_args();
    return $x + count($arg_arr);
}
                        

Problem: Kein Type-Hinting für weitere Parameter?

Weitere Features / Probleme

num als Elterntyp von int und float

Problem: num kann aktuell nicht als Annotation verwendet werden!

Weitere Features / Probleme

Heredoc Syntax


<?hh

function heredoc(): void {
  $foo = 3;
  $x = <<<MYHD
{$foo}
MYHD;
}

echo heredoc();
                        

Problem: Geschweifte Klammern.

Was nicht erlaubt ist

  • goto, if:...endif
  • Referenzen: function foo(&$bar)
  • Das @ Symbol, um Fehler zu unterdrücken.
  • Dynamische Features wie eval oder variable Variablen: $$myVar
  • Globale Variablen: global $globalVar
  • ArrayAccess
  • Vermischen von Hack-Code und HTML
  • … und noch einiges mehr

Pros & Cons

Ein paar Gedanken aus der aktuellen Diskussion

PRO Hack

  • Neue Features, auf die viele in PHP lange warten: Generics, Lambdas, Collections, …
  • Neue Tools wie den Type Checker.
  • Performance und schnellere Kompilierung.

CONTRA Hack

  • Spaltet die Community, PHP Internals vs. Facebook
  • Kompatibilität? Spezifikation? Was passiert, wenn PHP z.B. Generics implementiert, und zwar anders als Facebook? Oder gibt Hack jetzt die Implementierung vor?
  • Wer braucht es wirklich (Performance usw.)? Löst nur Probleme für Facebook?
  • (Noch) Kaum Entwickler, Community.
  • Open Source (controlled by Facebook)??

Discuss!

The End.