Carrying on from Part 3 of our series, we are going to take a look at some of the more complex (and in my opinion, cooler) parts of Hack.
While hack supports the creation of custom types, it's important to understand that these are only used by the static analysis — at runtime, they are seen as their original underlying types.
Hack supports two different ways of defining new types, aliases, and opaque types. These are defined using the
newtype keywords respectively.
Type aliases are just that — aliases. They simply allow you to give a more appropriate name for an existing type, or complex type — the static analyzer will permit you to use values of those types in the same way as the underlying type.
Opaque types on the other hand hide their underlying implementation outside of the file in which they are defined — this means that you cannot use them as standard types, and effectively they become immutable except when calling code defined within the file in which the type itself is defined. This restriction is not enforced at runtime.
Here we have a class to handle HTTP Status codes. We have defined a type alias
HTTPStatusCode which is a simple
Within our class, we have defined constants for each status code, type hinted using our type alias.
We then define a property,
$status that is a
HTTPStatusCode keys, and
Finally, we have our
send() method that accepts a
We then call this code using:
Because we use a type alias, we are allowed to pass any
int to the
send() method. This means that it's possible to pass in an unknown status code, which requires that we validate it before trying to access it by checking
We can make this code simpler by using an opaque type:
newtype instead of
type, we can no longer pass standard
int values in to the
send() method. Doing so will cause hack to show the following error:
Instead, we must use the constants we defined (or some other variable declared as a
Now we can be relatively sure that we're being passed a valid
HTTPStatusCode that will be in our
Map, and no longer need the
This is the power of type hinted code — it reduces your workload, clarifies your code, and hopefully minimizes bugs.
Shapes are an array-like construct, similar to tuples, but allowing us to pre-define the keys for our array and their value types.
We can use this as a form of validation — for example, if we want to represent an HTTP response we might create a shape that looks like:
At this point, any place a
HTTPRequest type hint is used, we can guarantee that all of these keys will exist, and have data of certain types.
XHP: XML fragments as expressions
One of the bigger changes to syntax is the introduction of XHP — and yet, it was originally released as an extension for PHP (also by Facebook), which you can still install and use with PHP today to get much of the same functionality.
Essentially, what XHP does is allow for XML tags to be top-level syntax, for example, note the lack of quotes as the markup is not a string.
Now, as this is XML, it must be valid — this means all tags must be closed (or empty e.g.
Each tag is mapped to a class, whose name starts with a colon, for example the
<p> class would be named
:p. As this is XML, namespaces are also supported, for a tag named
<hal:resource> it would be the
Each of these classes extends the
:x:element base class, which in turn supports a number of DOM-like methods:
Whenever you use an XHP element it instantiates an instance of the associated class.
Traditionally, we might do something like this to output a list of items:
With XHP, we can't do this as we have incomplete XML fragments with our opening and closing
With XHP, we can take advantage of the object nature to create this XML structure dynamically:
One other thing you might notice here is we have removed the call to
htmlentities(). Because our markup is no longer just part of a larger string, the engine is able to distinguish between markup and content — allowing it to automatically escape content.
While the syntax is supported out of the box, the tags are not defined. To "install" XHP, you will need to grab the
hack branch of the php-lib from the xhp repository.
Then simply include
init.hh in any application that wants to use XHP.
While Hack supports PHP-style closures, it also supports anonymous functions otherwise known as lambda expressions.
There are two reasons the Hack team decided to add this new form of anonymous functions:
- Anonymous functions have a much more compact syntax, which is also helped by the fact that:
- Scope is inherited by the anonymous function — this means that it inherits variables from its defining scope (by value)
Lambda expressions at their simplest use the syntax:
However, there is an expanded syntax that allows for multiple expressions within the function, and optionally parenthesis to distinguish arguments:
Even with the expanded syntax, especially with scope capture, these are much more concise.
Coming up next...
In our final installment on HHVM and Hack, we will look into using the static analyzer, as well as the HHVM command line tools, and how to convert your existing codebase to Hack.
It has been several months since HHVM 3.0 (with Hack) has been released, has it become part of your workflow? Are you using it in production? Let us know in the comments.