Pages

Tuesday, March 25, 2014

Hack Modes

Hack Modes 

Table of Contents 

  • Strict
  • Partial
  • Decl
  • Unsafe
When writing Hack code, it can be written in three modes: strict, partial or decl. These modes exist to give developers maximum flexibility when converting their PHP code to Hack and to allow engineers to write new Hack code that can call into existing, non-Hack code. For example, some code might just be inherently dynamic where type checking makes no sense. Or there could be a bug in the type checker. Or there is incorrectly typed code written elsewhere out of the engineer's control.
All modes are case-sensitive.

Strict 

Strict mode is just what it sounds like. The Hack type checker will catch every type error (see the //decl exception below). Any and all code written in strict mode must be correctly type annotated (unless // UNSAFE is used - see below). Another rule is that code written in strict mode cannot call into non-Hack code, and still pass the type checker. Here is how strict mode is declared.
<?hh // strict

// Everything is annotated and type checked
class StrictClass {
  private 
string $str;

  
// Need this constructor or the type checker
  // will complain about uninitialized member
  // variables
  
public function __construct() {
    
$this->str "Hello";
  }

  public function 
foo(int $xint $y): int {
    if (
$x $y) {
      return 
27;
    }
    return 
34;
  }

  public function 
bar(string $astring $b): string {
    return 
$a $b;
  }
}
Given that strict mode is more restrictive than the other Hack modes, extra care will need to be taken when writing code. Example of such care include:
  • Trying to use classes that are defined in code not written in Hack (which will produce an unbound name error)
  • Using arrays. They cannot be constructed in Hack without being typed. Collections are preferred.
  • Annotating all return types, including functions like main() in test code.

Partial 

Partial mode is the default of Hack. In partial mode, the type checker checks all types other than that encompassed by an// UNSAFE comment. Partial mode also allows for the partially typing of a class, method or function (e.g., only type a subset of its arguments). And, also unlike strict mode, partial mode allows engineers to call code that has not yet been "Hack-ified" (in other words, they can call into untyped code).
<?hh // partial

// Annotated and type checked.
function foo_partial(int $xint $y): int {
  if (
$x $y) {
    return 
27;
  }
  return 
34;
}
// Not annotated. Not type checked.function bar_partial($a$b) {
  return 
$a $b;
}
or, since partial is the default, the //partial can be omitted. In fact, omitting // partial is preferred.
<?hh
// Annotated and type checked.function foo_partial(int $xint $y): int {
  if (
$x $y) {
    return 
27;
  }
  return 
34;
}
// Not annotated. Not type checked.function bar_partial($a$b) {
  return 
$a $b;
}
Note:
In Hack's partial mode, it is still possible to misspell a function name in a call to fun(). As usual, Hack will just assume everything is correct and the HHVM runtime will catch the error. In strict mode, however, an unbound name error will be thrown by Hack.

Decl 

Decl mode is used to allow hack code written in strict mode to call into legacy code, without having to fix the issues that would be pointed out by partial mode. The type checker will "absorb" the signatures of the code, but will not type check the code. Decl is mainly used when annotating old, existing APIs (i.e., when the code does note meet Hack's stricter subset of PHP). As the following example shows, this could lead to bugs, so be careful.
File1.php
<?hh // decl

// Partial mode would catch this. Decl, though, allows // strict
// to call into this.
function bar_partial($a$b): int {
  return 
"Hello";
}
File2.php
<?hh // strict
function foo_strict(bool $b): int {
  if (
$b) {
    return 
4;
  } else {
    return 
bar_partial(34);
  }
}
Here bar_partial() returns an int for return typing purposes. However, since the code was declared with // decl, the type checker ignores the type annotation and the fact that bar_partial() actually returns a string. However, now// strict code can call bar_partial() without type checker errors.

Unsafe 

// UNSAFE disables the type checker from the point of unsafe declaration until the end of the current block of code (where the end of the current block generally refers to the associated ending brace (}) of which the // UNSAFE is declared).
From the unsafe comment until the end of the block, the engineer is trusted. It can be useful when there is a bug in the type checker and work must continue. // UNSAFE can be used in any mode (// strict, // partial or // decl). It should not be used without discretion, however. Since unsafe code is ignored by the type checker, there are chances that runtime errors may result.
Here are two examples that demonstrate how the coverage of // UNSAFE .
<?hhfunction unsafe_foo(int $xint $y): int {
  if (
$x $y) {
    
// UNSAFE
    
return "I am not checked by the type checker"// Covered by UNSAFE
  
}
  return 
34// NOT covered by UNSAFE}
<?hhfunction unsafe_foo(int $xint $y): int {
  if (
$x === 3) {
    return 
6// NOT covered by the UNSAFE
  
}
  
// UNSAFE
  
if ($x $y) {
    return 
"I am not checked by the type checker"// Covered by the UNSAFE
  
}
  return 
true// Covered by the UNSAFE}



No comments:

Post a Comment