About this ‘A Language a Day’ Advent Calendar
This article became a part of my book ‘A Language a Day’, which you can get in both electronic and paper format. Languages covered in the book: C++, Clojure, Crystal, D, Dart, Elixir, Factor, Go, Hack, Hy, Io, Julia, Kotlin, Lua, Mercury, Nim, OCaml, Raku, Rust, Scala, and TypeScript.
Welcome to Day 9 of this year’s A Language a Day Advent Calendar. Today’s topic is introduction to the Hack programming language.
Facts about the language
Some facts about Hack:
- Hack is an extension of PHP
- Hack uses the HHVM virtual machine (HH = HipHop)
- Hack provides both dynamic and static typing
- Introduced in 2014
- Created by Facebook
- Website: hacklang.org
Installing and running Hack
You need to install the HHVM machine. Refer to the installation instructions on its website for the details. Here is a snippet you can run on Mac:
$ brew tap hhvm/hhvm
$ brew install hhvm
(Run the second command twice or manually create the /usr/local/Frameworks
directory if you did not have one and got an error during the installation.)
There are two main ways of running a Hack program. First, you just do it from command line:
$ hhvm program.hack
In the second method, you start a webserver and see the results in a browser. Run the following command in a directory with your .hack files, and then navigate to http://localhost:80/program.hack
to see the output (remember that Hack is a dialect of PHP, that’s why web is its native land).
$ hhvm -m server -p 8080
Static checker
Hack comes with the hh_client
tool, which a fast typechecker daemon. It is possible to embed it to the IDE so that it displays the errors while you are typing. To run the tool, you need to have at least an empty configuration file .hhconfig
in your project directory.
Alternatively, run the tool as a daemon that will monitor the syntax as soon as you save the file. Having this, you will always have a fresh error list in the terminal:
$ watch hh_client
Hello, World!
The minimal program that works and prints the message is a one-liner:
echo "Welcome to Hack!\n";
You can also make it a bit more official by surrounding it with a function and explicitly telling where the program must start:
<<__EntryPoint>> function main(): noreturn { echo "Welcome to Hack!\n"; exit(0); }
The important point here is the <<__EntryPoint>>
attribute. It is followed by a function that will be called first. You can give a different name to this function; it should not necessarily be called main
.
Variables
Variables in Hack are prefixed with the dollar sign. There are special variables: $_
for loops and $this
for working with objects.
When a variable appears inside a double-quoted string, its value is substituted. Compare the following two printing instructions:
$name = 'John'; # name echo "Hello, $name!\n"; // Hello, John echo 'Hello, $name!\n'; /* No substitution */
The first echo
call prints the correct greeting with a name, while the second one prints a bare text string with a dollar in it; even the \n
is not substituted and is printed as is. Notice the comments: it can be in either bash or C style.
To create a constant, use the const
keyword:
const height = 190; echo height;
Aggregate data types
Arrays, dictionaries, and sets, you all create them with one of the array creation operators: vec[]
, dict[]
, or keyset[]
. Here are a few examples:
$array = vec[1, 2, 3]; $array[0] = 42; echo $array[0], "\n"; $dict = dict[]; # empty dictionary $dict["a"] = "alpha"; $dict["b"] = "beta"; echo $dict["a"], "\n";
A keyset is an ordered collection of things, all of which are of the same type and are not repeated. Here is a bit more complicated example that prints all the values in a keyset:
$keyset = keyset["red", "green", "blue", "red"]; foreach ($keyset as $x) { echo "$x\n"; // second "red" is not there }
This program prints red
, green
, and blue
. Notice how you organise a foreach
loop. First comes the variable over which you iterate, then the loop variable: $keyset as $x
. This is opposite to what you may expect thinking of a more traditional convention if you are not familiar with PHP.
Functions
You create a function using the function
keyword. The parameters of a function can have an optional type specifier as shown in the next example. The return type is also an optional thing. Here, it is explicitly said the function does not return anything:
function greet(string $name): void { echo "Hello, $name!\n"; } greet('John'); // greet(42); // error
If you uncomment the last line, you’ll get a runtime error, as the function does not expect a number. On the other hand, you cannot overload a function using the same name.
A Factorial example
A recursive function that computes a factorial can be defined like this:
function factorial(int $n): int { if ($n < 2) return 1; else return $n * factorial($n - 1); } echo factorial(5), "\n"; // 120
Lambdas
You can create an anonymous function by either omitting the function name or even the function
keyword itself. The following two code snippets do the same:
$f = function(int $x): int { return $x * $x; }; echo $f(2), "\n"; // 4 echo $f(3), "\n"; // 9
The same result is achieved here:
$f = (int $x): int ==> $x * $x; echo $f(2), "\n"; // 4 echo $f(3), "\n"; // 9
Namespaces
Namespaces in Hack are the nested names, where the parts are separated by backslashes. In the following program, you can see how you access the names from a different namespace.
namespace MyNameSpace\TestCode { function hello() { \echo("Hello, World!\n"); } } namespace { // global namespace <<__EntryPoint>> function main(): noreturn { MyNameSpace\TestCode\hello(); \exit(0); } }
In the Hack programs and documentation, you may see function calls prefixed with a single backslash. This is nothing more than a reference to the global namespace.
Classes
If you are familiar with traditional object-oriented programming, you will have no problems with understanding the code in Hack. Writing it, though, may be a bit less smooth at first.
Classes can have both private and public data fields and methods. You define a class with the class
keyword, and access its members using the $this
pointer together with the member selection operator ->
. In the following code, notice, for example, how the $name
field is accessed through $this->name
, with only a single $
for the whole construct. The class’s constructor is named __construct
. Examine the following program to get an idea of how to use simple classes.
class Person { public string $name; private int $age; public function __construct(string $name, int $age) { $this->name = $name; $this->age = $age; } public function inc_age() { $this->age++; } public function info() { return "I am $this->name, and I am $this->age years old.\n"; } public function __toString() { return $this->info(); } } <<__EntryPoint>> function main() { $p = new Person("Alice", 20); $p->inc_age(); echo $p->info(); echo $p->name, "\n"; // public field echo $p; // calls __toString(), and then info() }
The new
keyword instantiates an object of the given class. To automatically convert an object to a string, you can define the __toString()
method.
For simplicity, the constructors that only initialise data can be shortened like this:
class Person { public function __construct( public string $name, private int $age ) {} . . . }
Static members
Use the static
keyword to make a data field or a method static (i. e., belonging to the class, not its instances). To access such method, you need a double colon, as you can see in the following example. This program counts the number of created objects and saves the current number in the object. Thus, there is a common counter $total
which have impact on all created objects.
class Counter { private static int $total = 0; private int $my_number; public function __construct() { $this->my_number = ++Counter::$total; } public function info() { echo "My number is $this->my_number.\n"; } } <<__EntryPoint>> function main() { $a = new Counter(); $b = new Counter(); $a->info(); # My number is 1. $b->info(); # My number is 2. $a->info(); # My number is 1. }
Inheritance
Inheritance is possible via extending the classes, or via interfaces or by using traits. Let us program the zoo with a number of polymorphic animals.
A Polymorphic example
Here is our polymorphic zoo. The base class, Animal
, is an abstract class and it defines the contents of its both children: Dog
and Cat
.
abstract class Animal { abstract public function __toString(); } class Dog extends Animal { public function __toString() { return "Dog\n"; } } class Cat extends Animal { public function __toString() { return "Cat\n"; } } $zoo = vec[new Dog(), new Cat(), new Cat(), new Dog()]; foreach ($zoo as $x) { echo $x; }
The base class demands the presence of the __toString()
converter. This method is also marked abstract
in the Animal
class. The Cat
and Dog
classes defined their own behaviour for that method.
Although the $zoo
array contains a mixture of different objects, Hack correctly dispatches the calls of __toString()
when it meets echo $x
.
Asynchronous programming
The Hack language offers some support for asynchronous programming. There is a dedicated section ‘Asynchronous operations’ in the official documentation. Let me reduce the discussion to a Sleep sort implementation. To run it, you need to configure your setup to include the corresponding libraries, but it is easier to run the code online (add the <?hh
instruction at the top).
The Sleep Sort example
The general idea of the Sleep Sort implementation remains the same. You traverse the data array and create an asynchronous task that sleeps some time proportional to the given number and then prints it.
async function sort_me($n) { await SleepWaitHandle::create($n * 100000); echo "$n\n"; } async function main() { $data = [10, 4, 2, 6, 2, 7, 1, 3]; $jobs = Vector {}; foreach ($data as $n) { $jobs[] = sort_me($n); } await GenArrayWaitHandle::create($jobs); } main()->join();
The task is implemented in the sort_me
async function. It returns immediately after getting the job. To wait for all jobs to finish, we collect them in the $jobs
vector.
Notice how you can add a new element to the end of the vector: $jobs[] = sort_me($n)
. This does the same work as $jobs->add(sort_me($n))
. Each element in the $jobs
vector is an object of the HH\AsyncFunctionWaitHandle
type. When all the jobs are sent, we can wait for them to complete and join the main thread.
XHP
Behind this abbreviation, XHP, stands an extremely interesting mechanism that allows you to embed XML or proper HTML code directly to your Hack files. Here is an example:
$output = <title>Page title</title>;
Again, the above line is a valid Hack code. It further extends to the possibility to interpolate variables, for example:
function greeting($name) { echo <h1>Hello, {$name}!</h1>; } greeting('John');
If you want to try this feature, you have to deal with a painful installation process of getting the xhp-lib library work.
And let me stop here for today. I hope you got a few ideas of where Hack can be used at.
Get More
Learn more about the Hack programming language on:
On GitHub, you can find the source codes for this article about Hack.
Next: Day 10. Lua
One thought on “Hack at a Glance — A Language a Day, Advent Calendar Day 9/24”