diff --git a/hack.html.markdown b/hack.html.markdown index fb6af8e1..8e2d4b19 100644 --- a/hack.html.markdown +++ b/hack.html.markdown @@ -1,308 +1,381 @@ --- language: Hack contributors: + - ["Andrew DiMola", "https://github.com/AndrewDiMola"] - ["Stephen Holdaway", "https://github.com/stecman"] - ["David Lima", "https://github.com/davelima"] filename: learnhack.hh --- -Hack is a superset of PHP that runs under a virtual machine called HHVM. Hack -is almost completely interoperable with existing PHP code and adds a bunch of -useful features from statically typed languages. +[Hack](https://hacklang.org/) lets you write code quickly, while also having safety features built in, like static typechecking. +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 -available in the [PHP article](http://learnxinyminutes.com/docs/php/) on this site. +```Hack +/* ================================== + * READ THE DOCS! + * ================================== + */ -```php -id = $id; + /* ================================== + * 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); + } + + function processKey(arraykey $the_answer): bool { + if ($the_answer is int) { + return true; + } else { + return false; + } // true + } + + function calculate_square(num $arg)[]: float { + return ((float)$arg * $arg); + } + + // Enums are limited to int or string (as an Arraykey), or other enum values. + enum Permission: string { + Read = 'R'; + Write = 'W'; + Execute = 'E'; + Delete = 'D'; + } + + /* ================================== + * HACK ARRAYS + * ================================== + */ + + function demo_hack_arrays(): void { + + // 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 + + // keyset: ordered, without duplicates + $k = keyset[1, 2, 3]; // values must be int or string + $colors = keyset['red', 'blue', 'green']; + // $colors[0]; error: indexing not supported for keyset + $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) + * ================================== + */ + + // The Hack Standard Library is a set of functions and classes for the Hack language + // Imports are ideally at the top of your file but are placed here for instruction purposes + + 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 + + function demo_hack_standard_library(): void { + + $letters = vec['a', 'b', 'c']; + $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` + } + + /* ================================== + * 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'! + } + + /* ================================== + * FUNCTIONS + * ================================== + */ + + // 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. + function add_value(int $x, int $y = 1): int { + return $x + $y; + } + + // Functions can be variadic (unspecified length of arguments). + function sum_ints(int $val, int ...$vals): int { + $result = $val; + + foreach ($vals as $v) { + $result += $v; } -} + return $result; + } + // Functions can also be anonymous (defined with the `==>` arrow). + // $f = (int $x): int ==> $x + 1; -// Concise anonymous functions (lambdas) -$multiplier = 5; -array_map($y ==> $y * $multiplier, [1, 2, 3]); + /* ================================== + * ATTRIBUTES + * ================================== + */ + // Hack provides built-in attributes that can change runtime or static type checking behavior. + // For example, we used the `__EntryPoint` attribute earlier in the "Hello World!" example. -// Generics -class Box -{ - protected T $data; + // As another example, `__Memoize` caches the result of a function. + <<__Memoize>> + function doExpensiveTask(): ?string { + // return file_get_contents('http://hacklang.org'); + return "dynamic string with contents from hacklang.org"; + } - public function __construct(T $data) { - $this->data = $data; + /* ================================== + * CONTEXTS + * ================================== + */ + + // Hack functions are attached to different contexts and capabilities. + // A context is a grouping of capabilities; that is, a grouping of permissions. + + // To declare allowed contexts (and capabilities), use the Context List `[]`. + // If contexts are not defined, your function includes permissions defined in Hack's `defaults` context. + + // Because the context list is NOT defined, the `defaults` context is implicitly declared. + async function implicit_defaults_context(): Awaitable { + await IO\request_output()->writeAllAsync( + "Hello World!\n", + ); // prints 'Hello World'! + } + + // 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 { + 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 { + // 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(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 getData(): T { - return $this->data; + public function get(): int { + return $this->i; } -} + } -function openBox(Box $box) : int -{ - return $box->getData(); -} - - -// Shapes -// -// 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( - shape('x' => -1, 'y' => 5), - shape('x' => 2, 'y' => 50) -); - - -// Type aliasing -// -// Hack adds a bunch of type aliasing features for making complex types readable -newtype VectorArray = array>; - -// 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( - tuple(1, 2), - tuple(5, 6) -); - - -// First-class enums -enum RoadType : int -{ - Road = 0; - Street = 1; - Avenue = 2; - Boulevard = 3; -} - -function getRoadType() : RoadType -{ - 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; + // Properties and Methods can be static (not requiring instantiation). + class Person { + public static function favoriteProgrammingLanguage(): string { + return "Hack"; } -} + } + function demo_hack_classes(): void { + // Use `new` to instantiate a class. + $c1 = new Counter(); -// 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 -{ - for ($i = $start; $i <= $end; $i++) { - echo "$i "; + // To call a static property or method, use `::` + $typical_person = tuple("Andrew", Person::favoriteProgrammingLanguage()); + } - // Give other tasks a chance to do something - await RescheduleWaitHandle::create(RescheduleWaitHandle::QUEUE_DEFAULT, 0); + // Abstract class can be defined, but not instantiated directly. + abstract class Machine { + public function openDoors(): void { + return; } -} - -// 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>> -function doExpensiveTask() : ?string -{ - return file_get_contents('http://example.com'); -} - -// The function's body is only executed once here: -doExpensiveTask(); -doExpensiveTask(); - - -// The __ConsistentConstruct special attribute signals the Hack type checker to -// ensure that the signature of __construct is the same for all subclasses. -<<__ConsistentConstruct>> -class ConsistentFoo -{ - public function __construct(int $x, float $y) - { - // ... + public function closeDoors(): void { + return; } + } - public function someMethod() - { - // ... + /* ================================== + * INTERFACES + * ================================== + */ + + // A class can implement a set of capabilities via an interface. + // An interface is a set of method declarations and constants. + + interface Plane { + // A constant is a named value. Once defined, the value cannot be changed. + const MAX_SPEED = 300; + public function fly(): void; + } + + /* ================================== + * TRAITS + * ================================== + */ + + // 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 ConsistentBar extends ConsistentFoo -{ - public function __construct(int $x, float $y) - { - // Hack's type checker enforces that parent constructors are called - parent::__construct($x, $y); + class Spaceship extends Machine implements Plane { + use Airplane; - // ... + public function fly(): void { + // fly like the wind } + } - // 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() - { - // ... - } + /* ================================== + * KEEP READING! + * ================================== + */ + + /* This is a simplified guide! + * 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! + */ } -class InvalidFooSubclass extends ConsistentFoo -{ - // Not matching the parent constructor will cause a type checker error: - // - // "This object is of type ConsistentBaz. It is incompatible with this object - // of type ConsistentFoo because some of their methods are incompatible" - // - public function __construct(float $x) - { - // ... - } - - // Using the __Override annotation on a non-overridden method will cause a - // type checker error: - // - // "InvalidFooSubclass::otherMethod() is marked as override; no non-private - // parent definition found or overridden parent is defined in non-> - public function otherMethod() - { - // ... - } -} - - -// Traits can implement interfaces (standard PHP does not support this) -interface KittenInterface -{ - public function play() : void; -} - -trait CatTrait implements KittenInterface -{ - public function play() : void - { - // ... - } -} - -class Samuel -{ - use CatTrait; -} - - -$cat = new Samuel(); -$cat instanceof KittenInterface === true; // True - ``` ## More Information -Visit the [Hack language reference](http://docs.hhvm.com/manual/en/hacklangref.php) -for detailed explanations of the features Hack adds to PHP, or the [official Hack website](http://hacklang.org/) -for more general information. +Visit the [Hack language reference](http://docs.hhvm.com/hack/) to learn more about the Hack language. -Visit the [official HHVM website](http://hhvm.com/) for HHVM installation instructions. - -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. +For more information on HHVM, including installation instructions, visit the [official HHVM website](http://hhvm.com/).