Built In Types: Enum
Use an enum (enumerated type) to create a set of named, constant, immutable values.
In Hack, enums are limited to int
or string
(as an Arraykey
), or other enum
values.
Quickstart
To access an enum's value, use its full name, as in Colors::Blue
or Permission::Read
.
enum Colors: int {
Red = 3;
Green = 5;
Blue = 10;
Default = 3; // duplicate value is okay
}
enum Permission: string {
Read = 'R';
Write = 'W';
Execute = 'E';
Delete = 'D';
}
Additionally, by using the as
operator to enforce type, you can initialize your enum with static expressions that reference other enum values.
enum BitFlags: int as int {
F1 = 1; // value 1
F2 = BitFlags::F1 << 1; // value 2
F3 = BitFlags::F2 << 1; // value 4
F4 = 4 + 3; // value 7
}
Full Example
With an enum, we can create a placement-direction system with names like Top
, Bottom
, Left
, Right
, and Center
, then direct output accordingly to write text to the top, bottom, left, right, or center of a window.
enum Position: int {
Top = 0;
Bottom = 1;
Left = 2;
Right = 3;
Center = 4;
}
function writeText(string $text, Position $pos): void {
switch ($pos) {
case Position::Top:
// ...
break;
case Position::Center:
// ...
break;
case Position::Right:
// ...
break;
case Position::Left:
// ...
break;
case Position::Bottom:
// ...
break;
}
}
<<__EntryPoint>>
function main(): void {
writeText("Hello", Position::Bottom);
writeText("Today", Position::Left);
}
Library Methods
All enums implement these public static methods.
getValues()
/ getNames()
Returns a dict
of enum constant values and their names.
getValues()
returns adict
where the keys are the enum names and the values are the enum constant values.- In the example below, the keys/values would be:
"Top" => 0
,"Bottom" => 1
, etc.
- In the example below, the keys/values would be:
getNames()
returns adict
, but is flipped: the keys are the enum constant values and the values are the enum's named constants.- Following the same example, the keys/values would be:
0 => "Top"
,1 => "Bottom"
, etc. - Because a
dict
can not contain duplicate keys, when you callgetNames()
—the static method that returns adict
and flips an enum's constant values to keys—there is a possiblity of creating adict
with duplicates, resulting in anHH\InvariantException
. In this situation, one safe option for discarding duplicates (and keeping the most recent of every duplicate) isDict\flip
.
- Following the same example, the keys/values would be:
enum Position: int {
Top = 0;
Bottom = 1;
Left = 2;
Right = 3;
Center = 4;
}
<<__EntryPoint>>
function main(): void {
$names = Position::getNames();
echo " Position::getNames() ---\n";
foreach ($names as $key => $value) {
echo "\tkey >$key< has value >$value<\n";
}
$values = Position::getValues();
echo "Position::getValues() ---\n";
foreach ($values as $key => $value) {
echo "\tkey >$key< has value >$value<\n";
}
Dict\flip(Position::getValues()); // safe flip of values as keys
}
assert()
/ coerce()
assert($value)
checks if a value exists in an enum, and if it does, returns the value; if the value does not exist, throws an UnexpectedValueException
.
coerce($value)
checks if a value exists in an enum, and if it does, returns the value; if the value does not exist, returns null
.
enum Bits: int {
B1 = 2;
B2 = 4;
B3 = 8;
}
<<__EntryPoint>>
function main(): void {
Bits::assert(2); // 2
Bits::assert(16); // UnexpectedValueException
Bits::coerce(2); // 2
Bits::coerce(2.0); // null
Bits::coerce(16); // null
}
Note: Both library methods will, when no exact match is found, attempt to do a cast to the other arraykey
type. If the cast is not reversible / lossless, or the resulting value is still not a member of the enum after the cast, the failure result occurs, where a failure for assert is throwing an UnexpectedValueException
and a failure for coerce
is returning null.
assertAll()
assertAll($traversable)
calls assert($value)
on every element of the traversable (e.g. Hack Arrays); if at least one value does not exist, throws an UnexpectedValueException
.
enum Bits: int {
B1 = 2;
B2 = 4;
B3 = 8;
}
<<__EntryPoint>>
function main(): void {
$all_values = vec[2, 4, 8];
$some_values = vec[2, 4, 16];
$no_values = vec[32, 64, 128];
Bits::assertAll($all_values); // vec[2, 4, 8]
Bits::assertAll($some_values); // throws on 16, UnexpectedValueException
Bits::assertAll($no_values); // throws on 32, UnexpectedValueException
}
isValid()
isValid($value)
checks if a value exists in an enum, and if it does, returns true
; if the value does not exist, it returns false
.
enum Bits: int {
B1 = 2;
B2 = 4;
B3 = 8;
}
<<__EntryPoint>>
function main(): void {
\var_dump(Bits::isValid(2));
\var_dump(Bits::isValid(2.0));
\var_dump(Bits::isValid("2.0"));
\var_dump(Bits::isValid(8));
}
is
/ as
The operators is
and as
/?as
behave similarly, but not exactly, to isValid()
(similar to is
) and assert()
/coerce()
(similar to as
/?as
).
For is
/as
/?as
refinement, the operators validate that a value is a part of a given enum. Caution: These operators may perform implicit int/string coercion of enum values to preserve compatibility with isValid()
.
enum MyEnum: int {
FOO = 1;
}
<<__EntryPoint>>
function main(): void {
1 is MyEnum; // true
1 as MyEnum; // 1
42 is MyEnum; // false
42 as MyEnum; // TypeAssertionException
'foo' is MyEnum; // false
'foo' as MyEnum; // TypeAssertionException
'1' is MyEnum; // CAUTION - true
'1' as MyEnum; // CAUTION - '1'
}
Enum Inclusion
You can define an enum to include all of the constants of another enum with the use
keyword.
In the following example, enum
F
contains all of the constants of enum
E1
and enum
E2
.
enum E1: int as int {
A = 0;
}
enum E2: int as int {
B = 1;
}
enum F: int {
// same-line alternative: use E1, E2;
use E1;
use E2;
C = 2;
}
Enum Inclusion is subject to a few restrictions:
- Order: All
use
statements must precede enum constant declarations. - Uniqueness: All constant names across all enums must be unique.
- Subtype Relation: In the above example,
E1
andE2
are not considered subtypes ofF
; that is, the Hack Typechecker rejects passingE1::A
orE2::B
to a function that expects an argument of typeF
.
Note: Library functions like getNames()
and getValues()
perform a post-order traversal of all included enums.