Pages

Wednesday, March 26, 2014

Typing XHP

Table of Contents 

XHP augments the syntax of PHP such that XML document fragments become valid PHP expressions. Hack currently understands and type checks against two XHP types: :xhp and XHPChild. :xhp is the parent of all XHP elements. For example, <div>, <ui:link>,, etc. all derive from :xhp. XHPChild is a special interface implemented by arrays,strings, ints and floats, as well as :xhp itself. This interface's primary use is to provide a concrete type for the{} operator (e.g., <div>{$foo}</div>). It also provides a way to type appendChild's argument. In the future, Hack will also provide a way to statically check some XHP rules (e.g., categories, etc.).

XHP Types 

When using XHP, it is important to keep how XHP is typed in mind:
  1. The :xhp class is the root class for all XHP elements. For example, <div>, <ui:link>>, etc. all derive from:xhp.
  2. The type of an <xyz> element is :xyz. Use :xyz when annotating types, but <xyz> when building XHP nodes.
  3. There is a special XHPChild interface, compatible with array, string, int, float and, of course, :xhp.

XHPChild 

The purpose of XHPChild is to provide a concrete type for the {} operator. In the following XHP fragment:
{$foo}
$foo is an XHPChild object.
The XHP method appendChild() also takes an XHPChild. Thus, given the example XHP fragment above, the following code can be written as an equivalent alternative:
$div = <div />
$div->appendChild($foo);
When writing an API that takes an argument flowing into the {} operator, use the XHPChild type hint.
<?hhfunction render_in_a_box(XHPChild $msg): :xhp {
  return <
ui:box>{$msg}</ui:box>;
}
Of course, there will be cases when a specific type other than XHPChild needs to be used, and that is obviously ok.
<?hhfunction do_some_alignment(:ui:slideshow $x) {
  
// do something with $x->getCurrentCount()}

Returning XHP Elements 

When returning an XHP element, there is a choice between returning the specific XHP type (e.g., :ui:box) or the more general :xhp or XHPChild.
  • Returning a specific XHP type is useful in the rare cases where the caller needs to call specific methods on the XHP element.
  • Returning XHPChild is useful in cases such as:
    <?hhfunction foo(int $x): mixed {
      if (
    $x 4) {
        return 
    "some string";
      } else {
        
    $div = <div />;
        return 
    $div// XHPChild
      
    }
    }
  • Returning :xhp is a good choice in a majority of cases since it hides the actual element as an implementation detail. For example, a method like render_in_a_box() in the example above might be changed to return a<xui:box>. If the return type is :xhp, the function signature does not need to be changed.

Other XHP Information 

Here are some other tidbits around XHP and Hack:
  • A handful of classes behave like XHP but don't derive from :xhp. A class was created called XHPChildClass to deal with these edge cases. XHPChildClass will probably not have to ever be dealt with directly, but, just in case,XHPChildClass is a subclass of XHPChild.
  • The Xhp type is no longer needed.
  • XHPish is no longer valid.
  • :xhp was previously called :x:base.

Future Plans 

XHP attributes have types. However, because they are written or read via setAttribute/getAttribute, which inherently cannot be typed except for mixed, it is difficult to type check attributes without special integration. A workaround is to integrate Hack with XHP such that attribute type checking occurs at call sites of those methods, or at__construct() call sites (i.e. native XHP syntax). In other words, the type checker could complain about the following intelligently instead of attempting to cast values or throwing a fatal exception/error at runtime:
<?hh
$div 
= <div />;$div->setAttribute('class'1); // Type error$div->getAttribute('class')->someMethod(); // Type error$num = <ui:number value={new Exception()} />; // Type error
In the future, Hack may provide static type checking for native XHP attributes or other rule-based assumptions (e.g. categories). There are a few hurdles to overcome or work around--currently, all attributes are lumped into a map structure, which is not Hack-friendly, but attributes are declared with XHP-specific syntax indicating their types, so Hack could analyze those accordingly. The end goal is deep integration of Hack throughout the XHP framework, whether by way of special XHP analysis, or rewriting parts of core libraries to fit within Hack's current confines.








No comments:

Post a Comment