mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2025-08-05 22:37:42 +02:00
Overhaul Hack in Y Minutes
This file was updated about 6 years ago. Since then, there have been many changes, including a distancing from PHP. This commit is an overhaul of the previous documentation for learning Hack in Y minutes. Over the years, there have also been many language changes and updates, for example, the introduction of Hack Arrays (vec, keyset, and dict) and the Hack Standard Language (HSL). To read more about how the language has changed and evolved, you can read the HHVM Blog: https://hhvm.com/blog/.
This commit is contained in:
@@ -1,308 +1,381 @@
|
|||||||
---
|
---
|
||||||
language: Hack
|
language: Hack
|
||||||
contributors:
|
contributors:
|
||||||
|
- ["Andrew DiMola", "https://github.com/AndrewDiMola"]
|
||||||
- ["Stephen Holdaway", "https://github.com/stecman"]
|
- ["Stephen Holdaway", "https://github.com/stecman"]
|
||||||
- ["David Lima", "https://github.com/davelima"]
|
- ["David Lima", "https://github.com/davelima"]
|
||||||
filename: learnhack.hh
|
filename: learnhack.hh
|
||||||
---
|
---
|
||||||
|
|
||||||
Hack is a superset of PHP that runs under a virtual machine called HHVM. Hack
|
[Hack](https://hacklang.org/) lets you write code quickly, while also having safety features built in, like static typechecking.
|
||||||
is almost completely interoperable with existing PHP code and adds a bunch of
|
|
||||||
useful features from statically typed languages.
|
|
||||||
|
|
||||||
|
To run Hack code, [install HHVM](https://docs.hhvm.com/hhvm/installation/introduction), the open-source virtual machine.
|
||||||
|
|
||||||
Only Hack-specific features are covered here. Details about PHP's syntax are
|
```Hack
|
||||||
available in the [PHP article](http://learnxinyminutes.com/docs/php/) on this site.
|
/* ==================================
|
||||||
|
* READ THE DOCS!
|
||||||
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
```php
|
/* For more information on the Hack language:
|
||||||
<?hh
|
* - About Hack: https://hacklang.org/
|
||||||
|
* - Documentation: https://docs.hhvm.com/hack/
|
||||||
|
*/
|
||||||
|
|
||||||
// Hack syntax is only enabled for files starting with an <?hh marker
|
/* ==================================
|
||||||
// <?hh markers cannot be interspersed with HTML the way <?php can be.
|
* A NOTE ON PHP
|
||||||
// Using the marker "<?hh //strict" puts the type checker in strict mode.
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The Hack language began as a superset of PHP.
|
||||||
|
// Since then, the languages have (largely) diverged.
|
||||||
|
// You may encounter the .php extension, which is no longer recommended.
|
||||||
|
|
||||||
// Scalar parameter type hints
|
/* ==================================
|
||||||
function repeat(string $word, int $count)
|
* COMMENTS
|
||||||
{
|
* ==================================
|
||||||
$word = trim($word);
|
*/
|
||||||
return str_repeat($word . ' ', $count);
|
|
||||||
|
// Hack has single-line comments...
|
||||||
|
|
||||||
|
/* Multi-line comments...
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ... and a special syntax for doc comments.
|
||||||
|
*
|
||||||
|
* Use doc comments to summarize the purpose of a definition, function, class or method.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ==================================
|
||||||
|
* NAMESPACES
|
||||||
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Namespaces contain definitions of classes, interfaces, traits, functions, and constants.
|
||||||
|
|
||||||
|
namespace LearnHackinYMinutes {
|
||||||
|
|
||||||
|
/* ==================================
|
||||||
|
* TYPES
|
||||||
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
function demo_hack_types(): void {
|
||||||
|
|
||||||
|
// Hack has five primitive types: bool, int, float, string, and null
|
||||||
|
$is_helpful = true; // bool
|
||||||
|
$int_value = 10; // int
|
||||||
|
$precise_value = 2.0; // float
|
||||||
|
$hello_world = "Hello World!"; // string
|
||||||
|
$null_string = null; // null
|
||||||
|
|
||||||
|
// Create a `shape` with the shape keyword, with a series of field names and values.
|
||||||
|
$my_point = shape('x' => -3, 'y' => 6, 'visible' => true);
|
||||||
|
|
||||||
|
// Create a `tuple` with the tuple keyword, with a series of two or more types as values.
|
||||||
|
$apple_basket = tuple("apples", 25); // different types are OK
|
||||||
|
|
||||||
|
// Use `arraykey` to represent either an integer or string.
|
||||||
|
$the_answer = 42;
|
||||||
|
$is_answer = processKey($the_answer);
|
||||||
|
|
||||||
|
// Similarily, `num` represents either an int or float.
|
||||||
|
$lucky_number = 7;
|
||||||
|
$lucky_square = calculate_square($lucky_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type hints for return values
|
function processKey(arraykey $the_answer): bool {
|
||||||
function add(...$numbers) : int
|
if ($the_answer is int) {
|
||||||
{
|
return true;
|
||||||
return array_sum($numbers);
|
} else {
|
||||||
|
return false;
|
||||||
|
} // true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions that return nothing are hinted as "void"
|
function calculate_square(num $arg)[]: float {
|
||||||
function truncate(resource $handle) : void
|
return ((float)$arg * $arg);
|
||||||
{
|
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type hints must explicitly allow being nullable
|
// Enums are limited to int or string (as an Arraykey), or other enum values.
|
||||||
function identity(?string $stringOrNull) : ?string
|
enum Permission: string {
|
||||||
{
|
Read = 'R';
|
||||||
return $stringOrNull;
|
Write = 'W';
|
||||||
|
Execute = 'E';
|
||||||
|
Delete = 'D';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type hints can be specified on class properties
|
/* ==================================
|
||||||
class TypeHintedProperties
|
* HACK ARRAYS
|
||||||
{
|
* ==================================
|
||||||
public ?string $name;
|
*/
|
||||||
|
|
||||||
protected int $id;
|
function demo_hack_arrays(): void {
|
||||||
|
|
||||||
private float $score = 100.0;
|
// vec: ordered
|
||||||
|
$v = vec[1, 2, 3];
|
||||||
|
$letters = vec['a', 'b', 'c'];
|
||||||
|
$letters[0]; // indexing at `0` returns 'a'
|
||||||
|
$letters[] = 'd'; // appends 'd'
|
||||||
|
// unset($letters['a']); error: remove-at-index is unsupported for vec
|
||||||
|
|
||||||
// Hack's type checker enforces that typed properties either have a
|
// keyset: ordered, without duplicates
|
||||||
// default value or are set in the constructor.
|
$k = keyset[1, 2, 3]; // values must be int or string
|
||||||
public function __construct(int $id)
|
$colors = keyset['red', 'blue', 'green'];
|
||||||
{
|
// $colors[0]; error: indexing not supported for keyset
|
||||||
$this->id = $id;
|
$colors[] = 'yellow'; // appends 'yellow'
|
||||||
}
|
unset($colors['red']); // removes 'red'
|
||||||
|
|
||||||
|
// dict: ordered, by key-value
|
||||||
|
$d = dict['a' => 1, 'b' => 3]; // keys must be int or string
|
||||||
|
$alphabet = dict['a' => 1, 'b' => 2];
|
||||||
|
$alphabet['a']; // indexing at 'a' returns `1`
|
||||||
|
$alphabet['c'] = 3; // adds a new key-value pair of `c => 3`
|
||||||
|
unset($alphabet['b']); // removes 'b'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================================
|
||||||
|
* THE HACK STANDARD LIBRARY (HSL)
|
||||||
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
// Concise anonymous functions (lambdas)
|
// The Hack Standard Library is a set of functions and classes for the Hack language
|
||||||
$multiplier = 5;
|
// Imports are ideally at the top of your file but are placed here for instruction purposes
|
||||||
array_map($y ==> $y * $multiplier, [1, 2, 3]);
|
|
||||||
|
|
||||||
|
use namespace HH\Lib\C; // the `C` library operates on containers (like Hack Arrays)
|
||||||
|
use namespace HH\Lib\Str; // The `Str` library operates on strings
|
||||||
|
|
||||||
// Generics
|
function demo_hack_standard_library(): void {
|
||||||
class Box<T>
|
|
||||||
{
|
|
||||||
protected T $data;
|
|
||||||
|
|
||||||
public function __construct(T $data) {
|
$letters = vec['a', 'b', 'c'];
|
||||||
$this->data = $data;
|
$colors = keyset['red', 'blue', 'green'];
|
||||||
|
$alphabet = dict['a' => 1, 'b' => 2];
|
||||||
|
|
||||||
|
C\contains($letters, 'c'); // checks for a value; returns 'true'
|
||||||
|
C\contains($colors, 'purple'); // checks for a value; returns 'false'
|
||||||
|
C\contains($alphabet, 'a'); // checks for a value; returns 'true'
|
||||||
|
|
||||||
|
Str\length("foo"); // returns `3`
|
||||||
|
Str\join(vec['foo', 'bar', 'baz'], '!'); // returns `foo!bar!baz`
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getData(): T {
|
/* ==================================
|
||||||
return $this->data;
|
* HELLO WORLD!
|
||||||
}
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
use namespace HH\Lib\IO; // the `IO` library is a standard API for input / output
|
||||||
|
|
||||||
|
<<__EntryPoint>> // required attribute for the typical entry/main function
|
||||||
|
async function main(): Awaitable<
|
||||||
|
void,
|
||||||
|
> { // does not need to be named 'main' / is an asynchronous function
|
||||||
|
await IO\request_output()->writeAllAsync(
|
||||||
|
"Hello World!\n",
|
||||||
|
); // prints 'Hello World'!
|
||||||
}
|
}
|
||||||
|
|
||||||
function openBox(Box<int> $box) : int
|
/* ==================================
|
||||||
{
|
* FUNCTIONS
|
||||||
return $box->getData();
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Functions are defined globally.
|
||||||
|
// When a function is defined in a class, we refer to the function as a method.
|
||||||
|
|
||||||
|
// Functions have return types (here: `int`) and must return a type or nothing (`void`).
|
||||||
|
function add_one(int $x): int {
|
||||||
|
return $x + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Functions can also have defined, default values.
|
||||||
// Shapes
|
function add_value(int $x, int $y = 1): int {
|
||||||
//
|
return $x + $y;
|
||||||
// Hack adds the concept of shapes for defining struct-like arrays with a
|
|
||||||
// guaranteed, type-checked set of keys
|
|
||||||
type Point2D = shape('x' => int, 'y' => int);
|
|
||||||
|
|
||||||
function distance(Point2D $a, Point2D $b) : float
|
|
||||||
{
|
|
||||||
return sqrt(pow($b['x'] - $a['x'], 2) + pow($b['y'] - $a['y'], 2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
distance(
|
// Functions can be variadic (unspecified length of arguments).
|
||||||
shape('x' => -1, 'y' => 5),
|
function sum_ints(int $val, int ...$vals): int {
|
||||||
shape('x' => 2, 'y' => 50)
|
$result = $val;
|
||||||
);
|
|
||||||
|
|
||||||
|
foreach ($vals as $v) {
|
||||||
// Type aliasing
|
$result += $v;
|
||||||
//
|
}
|
||||||
// Hack adds a bunch of type aliasing features for making complex types readable
|
return $result;
|
||||||
newtype VectorArray = array<int, Vector<int>>;
|
|
||||||
|
|
||||||
// A tuple containing two integers
|
|
||||||
newtype Point = (int, int);
|
|
||||||
|
|
||||||
function addPoints(Point $p1, Point $p2) : Point
|
|
||||||
{
|
|
||||||
return tuple($p1[0] + $p2[0], $p1[1] + $p2[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addPoints(
|
// Functions can also be anonymous (defined with the `==>` arrow).
|
||||||
tuple(1, 2),
|
// $f = (int $x): int ==> $x + 1;
|
||||||
tuple(5, 6)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
/* ==================================
|
||||||
|
* ATTRIBUTES
|
||||||
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
// First-class enums
|
// Hack provides built-in attributes that can change runtime or static type checking behavior.
|
||||||
enum RoadType : int
|
// For example, we used the `__EntryPoint` attribute earlier in the "Hello World!" example.
|
||||||
{
|
|
||||||
Road = 0;
|
|
||||||
Street = 1;
|
|
||||||
Avenue = 2;
|
|
||||||
Boulevard = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRoadType() : RoadType
|
// As another example, `__Memoize` caches the result of a function.
|
||||||
{
|
|
||||||
return RoadType::Avenue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Constructor argument promotion
|
|
||||||
//
|
|
||||||
// To avoid boilerplate property and constructor definitions that only set
|
|
||||||
// properties, Hack adds a concise syntax for defining properties and a
|
|
||||||
// constructor at the same time.
|
|
||||||
class ArgumentPromotion
|
|
||||||
{
|
|
||||||
public function __construct(public string $name,
|
|
||||||
protected int $age,
|
|
||||||
private bool $isAwesome) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class WithoutArgumentPromotion
|
|
||||||
{
|
|
||||||
public string $name;
|
|
||||||
|
|
||||||
protected int $age;
|
|
||||||
|
|
||||||
private bool $isAwesome;
|
|
||||||
|
|
||||||
public function __construct(string $name, int $age, bool $isAwesome)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
$this->age = $age;
|
|
||||||
$this->isAwesome = $isAwesome;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Co-operative multi-tasking
|
|
||||||
//
|
|
||||||
// Two new keywords "async" and "await" can be used to perform multi-tasking
|
|
||||||
// Note that this does not involve threads - it just allows transfer of control
|
|
||||||
async function cooperativePrint(int $start, int $end) : Awaitable<void>
|
|
||||||
{
|
|
||||||
for ($i = $start; $i <= $end; $i++) {
|
|
||||||
echo "$i ";
|
|
||||||
|
|
||||||
// Give other tasks a chance to do something
|
|
||||||
await RescheduleWaitHandle::create(RescheduleWaitHandle::QUEUE_DEFAULT, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This prints "1 4 7 2 5 8 3 6 9"
|
|
||||||
AwaitAllWaitHandle::fromArray([
|
|
||||||
cooperativePrint(1, 3),
|
|
||||||
cooperativePrint(4, 6),
|
|
||||||
cooperativePrint(7, 9)
|
|
||||||
])->getWaitHandle()->join();
|
|
||||||
|
|
||||||
|
|
||||||
// Attributes
|
|
||||||
//
|
|
||||||
// Attributes are a form of metadata for functions. Hack provides some
|
|
||||||
// special built-in attributes that introduce useful behaviour.
|
|
||||||
|
|
||||||
// The __Memoize special attribute causes the result of a function to be cached
|
|
||||||
<<__Memoize>>
|
<<__Memoize>>
|
||||||
function doExpensiveTask() : ?string
|
function doExpensiveTask(): ?string {
|
||||||
{
|
// return file_get_contents('http://hacklang.org');
|
||||||
return file_get_contents('http://example.com');
|
return "dynamic string with contents from hacklang.org";
|
||||||
}
|
}
|
||||||
|
|
||||||
// The function's body is only executed once here:
|
/* ==================================
|
||||||
doExpensiveTask();
|
* CONTEXTS
|
||||||
doExpensiveTask();
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Hack functions are attached to different contexts and capabilities.
|
||||||
|
// A context is a grouping of capabilities; that is, a grouping of permissions.
|
||||||
|
|
||||||
// The __ConsistentConstruct special attribute signals the Hack type checker to
|
// To declare allowed contexts (and capabilities), use the Context List `[]`.
|
||||||
// ensure that the signature of __construct is the same for all subclasses.
|
// If contexts are not defined, your function includes permissions defined in Hack's `defaults` context.
|
||||||
<<__ConsistentConstruct>>
|
|
||||||
class ConsistentFoo
|
// Because the context list is NOT defined, the `defaults` context is implicitly declared.
|
||||||
{
|
async function implicit_defaults_context(): Awaitable<void> {
|
||||||
public function __construct(int $x, float $y)
|
await IO\request_output()->writeAllAsync(
|
||||||
{
|
"Hello World!\n",
|
||||||
// ...
|
); // prints 'Hello World'!
|
||||||
}
|
}
|
||||||
|
|
||||||
public function someMethod()
|
// In the function below, the context list is defined to have the `defaults` context.
|
||||||
{
|
// A function can have multiple contexts [context1, context2, ...].
|
||||||
// ...
|
// `defaults` includes most of the capabilities defined by the Hack language.
|
||||||
|
async function explicit_defaults_context()[defaults]: Awaitable<void> {
|
||||||
|
await IO\request_output()->writeAllAsync("Hello World!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can also specify zero contexts to create a pure function (no capabilities).
|
||||||
|
async function empty_context()[]: Awaitable<void> {
|
||||||
|
// The following line is an error, as the function does not have IO capabilities.
|
||||||
|
// await IO\request_output()->writeAllAsync("Hello World!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================================
|
||||||
|
* GENERICS
|
||||||
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Generics allow classes or methods to be parameterized to any set of types.
|
||||||
|
// That's pretty cool!
|
||||||
|
|
||||||
|
// Hack typically passes by value: use `inout` to pass by reference.
|
||||||
|
function swap<T>(inout T $input1, inout T $input2): void {
|
||||||
|
$temp = $input1;
|
||||||
|
$input1 = $input2;
|
||||||
|
$input2 = $temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================================
|
||||||
|
* CLASSES
|
||||||
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Classes provide a way to group functionality and state together.
|
||||||
|
// To define a class, use the `class` keyword. To instantiate, use `new`.
|
||||||
|
// Like other languages, you can use `$this` to refer to the current instance.
|
||||||
|
|
||||||
|
class Counter {
|
||||||
|
private int $i = 0;
|
||||||
|
|
||||||
|
public function increment(): void {
|
||||||
|
$this->i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(): int {
|
||||||
|
return $this->i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConsistentBar extends ConsistentFoo
|
// Properties and Methods can be static (not requiring instantiation).
|
||||||
{
|
class Person {
|
||||||
public function __construct(int $x, float $y)
|
public static function favoriteProgrammingLanguage(): string {
|
||||||
{
|
return "Hack";
|
||||||
// Hack's type checker enforces that parent constructors are called
|
|
||||||
parent::__construct($x, $y);
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
// The __Override annotation is an optional signal for the Hack type
|
|
||||||
// checker to enforce that this method is overriding a method in a parent
|
|
||||||
// or trait. If not, this will error.
|
|
||||||
<<__Override>>
|
|
||||||
public function someMethod()
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InvalidFooSubclass extends ConsistentFoo
|
function demo_hack_classes(): void {
|
||||||
{
|
// Use `new` to instantiate a class.
|
||||||
// Not matching the parent constructor will cause a type checker error:
|
$c1 = new Counter();
|
||||||
//
|
|
||||||
// "This object is of type ConsistentBaz. It is incompatible with this object
|
// To call a static property or method, use `::`
|
||||||
// of type ConsistentFoo because some of their methods are incompatible"
|
$typical_person = tuple("Andrew", Person::favoriteProgrammingLanguage());
|
||||||
//
|
|
||||||
public function __construct(float $x)
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using the __Override annotation on a non-overridden method will cause a
|
// Abstract class can be defined, but not instantiated directly.
|
||||||
// type checker error:
|
abstract class Machine {
|
||||||
//
|
public function openDoors(): void {
|
||||||
// "InvalidFooSubclass::otherMethod() is marked as override; no non-private
|
return;
|
||||||
// parent definition found or overridden parent is defined in non-<?hh code"
|
}
|
||||||
//
|
public function closeDoors(): void {
|
||||||
<<__Override>>
|
return;
|
||||||
public function otherMethod()
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================================
|
||||||
|
* INTERFACES
|
||||||
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
// Traits can implement interfaces (standard PHP does not support this)
|
// A class can implement a set of capabilities via an interface.
|
||||||
interface KittenInterface
|
// An interface is a set of method declarations and constants.
|
||||||
{
|
|
||||||
public function play() : void;
|
interface Plane {
|
||||||
|
// A constant is a named value. Once defined, the value cannot be changed.
|
||||||
|
const MAX_SPEED = 300;
|
||||||
|
public function fly(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
trait CatTrait implements KittenInterface
|
/* ==================================
|
||||||
{
|
* TRAITS
|
||||||
public function play() : void
|
* ==================================
|
||||||
{
|
*/
|
||||||
// ...
|
|
||||||
|
// A trait defines properties and method declarations.
|
||||||
|
// Traits are recommended when abstracting code for reuse.
|
||||||
|
// Traits are included in code via the `use` keyword.
|
||||||
|
// `use` allows for other includes, like namespaces, classes, and functions (and more)!
|
||||||
|
|
||||||
|
trait Airplane {
|
||||||
|
// Like other languages, classes are extended, and interfaces are implemented.
|
||||||
|
require extends Machine; // abstract class
|
||||||
|
require implements Plane; // interface
|
||||||
|
|
||||||
|
public function takeOff(): void {
|
||||||
|
$this->openDoors();
|
||||||
|
$this->closeDoors();
|
||||||
|
$this->fly();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Samuel
|
class Spaceship extends Machine implements Plane {
|
||||||
{
|
use Airplane;
|
||||||
use CatTrait;
|
|
||||||
|
public function fly(): void {
|
||||||
|
// fly like the wind
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================================
|
||||||
|
* KEEP READING!
|
||||||
|
* ==================================
|
||||||
|
*/
|
||||||
|
|
||||||
$cat = new Samuel();
|
/* This is a simplified guide!
|
||||||
$cat instanceof KittenInterface === true; // True
|
* There's much more to learn, including:
|
||||||
|
* - Asynchronous Operations: https://docs.hhvm.com/hack/asynchronous-operations/introduction
|
||||||
|
* - Reified Generics: https://docs.hhvm.com/hack/reified-generics/reified-generics
|
||||||
|
* - XHP: https://docs.hhvm.com/hack/XHP/setup
|
||||||
|
* - ... and more!
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## More Information
|
## More Information
|
||||||
|
|
||||||
Visit the [Hack language reference](http://docs.hhvm.com/manual/en/hacklangref.php)
|
Visit the [Hack language reference](http://docs.hhvm.com/hack/) to learn more about the Hack language.
|
||||||
for detailed explanations of the features Hack adds to PHP, or the [official Hack website](http://hacklang.org/)
|
|
||||||
for more general information.
|
|
||||||
|
|
||||||
Visit the [official HHVM website](http://hhvm.com/) for HHVM installation instructions.
|
For more information on HHVM, including installation instructions, visit the [official HHVM website](http://hhvm.com/).
|
||||||
|
|
||||||
Visit [Hack's unsupported PHP features article](http://docs.hhvm.com/manual/en/hack.unsupported.php)
|
|
||||||
for details on the backwards incompatibility between Hack and PHP.
|
|
||||||
|
Reference in New Issue
Block a user