Type Inference

Most people are familiar with statically typed languages (like C or Java). Having a type inference system may, however, be a new concept. A type inference system brings many pros (e.g., write less and more readable code), but there are also a few gotchas.

Block level inference

Hack implements a unique block level inference system.

Assume a nullable ?int variable and a function which takes an int. There are two choices for calling this function with a nullable. ?int can be cast to an int, in which case null will be converted to 0. Or code can be written like this:

<?hh

function foo(int $y): void { }

function 
bar(?int $x): void {
  if (
is_int($x)) {
    
foo($x);
  } else {
    ...
  }
}

When is_int($x) is written inside a conditional statement, the Hack type checker will know that within the if/then branch, $x is an int. In the if/else branch, the type will be null. Here is another example:

<?hh

class Foo {}
class 
Bar extends Foo { public function blah(): void {} }

function 
foo(Foo $x): void {
  if (
$x instanceof Bar) {
    
// $x is now a Bar
    
$x->blah();
    ...
  } else {
    
// $x is still just a Foo
    
...
  }
}

Local vs. member variables

protected, private and public member variables are not typed in the same way as local variables. The reason is that protected and private variables might change when calling other functions.

This code is fine:

<?hh

class Foo {
  public function 
f1(?int $x): void {
    if (
is_int($x)) {
      
$this->doSomething();
      
$y $x 2;
    }
  }

  public function 
doSomething(): void {}
}

This code however is incorrect:

<?hh

class Foo {
  protected ?
int $x;

  public function 
f1(): void {
    if (
is_int($this->x)) {
      
$this->doSomething();
      
// can no longer assume $this->x is an int, doSomething might have changed it back to null.
      // note: can't analyze doSomething() because a child class of Foo might change its behavior.
      
$y $this->2;
    }
  }

  public function 
doSomething(): void {}
}

Here is a possible fix to the above problem:

<?hh

class Foo {
  protected ?
int $x;

  public function 
f1(): void {
    
$x $this->x;
    if (
is_int($x)) {
      
$this->doSomething();
      
$y $x 2;
    }
  }

  public function 
doSomething(): void {}
  }

To Top