Enums: Introduction

If you are familiar with enums (enumerations) in languages like C#, C++ or Java, then Hack enums will make you feel right at home. Enums encapsulate a group of related constants, unlike just using global or class constants. Enums actually create a new type, annotatable by name.

NOTE: At this point, Hack only supports int and string enums.

Syntax

The syntax of an enum is relatively straightforward, with a similar feel to other languages supporting enums.

enum <NameOfEnum>: <string | int> {
  <Member1> = <value>;
  <Member2> = <value>;
  :
  :
  <MemberN> = <value>;
} 

The name of the enum has to be namespace unique, and members of the enum must be uniquely named too.

<?hh

namespace Hack\UserDocumentation\Enums\Intro\Examples\Simple;

enum Size: int {
  SMALL = 0;
  MEDIUM = 1;
  LARGE = 2;
  X_LARGE = 3;
}
Output

Enum Member Values

The values of enum members must match the type of the enum. If your enum is marked as being an int, enum, then the member values must be int. Also, the values must be able to be statically evaluated. In other words, no variables, etc. can be used as part of an enum member value.

To access a member value, you follow the similar syntax as class constants

<NameOfEnum>::<MemberN>
<?hh

namespace Hack\UserDocumentation\Enums\Intro\Examples\MemberValues;

enum Size: string {
  SMALL = "small" ;
  MEDIUM = "medium";
  LARGE = "large";
  X_LARGE = "x-large";
}

function say_all_sizes(): void {
  echo Size::SMALL . PHP_EOL . Size::MEDIUM . PHP_EOL .
       Size::LARGE . PHP_EOL . Size::X_LARGE;
}

say_all_sizes();
Output
small
medium
large
x-large

NOTE: There is no notion of sequential, implicit values to enum members. For example, if you set the first member to 0, the next member doesn't, by default, have the value of 1. Each enum member must have an explicit value assigned to it.

Casting to Underlying Type

While enums have underlying types, they are not interchangeable. The enum is a distinct type. And like any other types, you cannot, for example, pass an int to a function expecting an enum, or an enum value to a function expecting a string.

However, you can use simple casting to convert a member value of an enum to its underlying type, and you can use Hack's special enum functions assert() and coerce() to convert from an underlying type to an enum type.

NOTE: You can use some language constructs like echo without casting to the underlying type.

<?hh

namespace Hack\UserDocumentation\Enums\Intro\Examples\Casting;

enum Size: string {
  SMALL = "small" ;
  MEDIUM = "medium";
  LARGE = "large";
  X_LARGE = "x-large";
}

function say_greeting_with_size(string $size): void {
  echo "Hello. We have size " . $size . PHP_EOL;
}

function give_shirt(Size $size): void {
  echo "Here is your shirt of size " . $size . PHP_EOL;
}

function sales(): void {
  // We need a cast here because say_greeting_with_size is expecting a string
  // and it is incompatible with the value of an enum (even though it is a
  // string underlying).
  say_greeting_with_size((string) Size::SMALL);
  // To go from the underlying type to the enum type, Hack provides builtin
  // enum functions to help you. The assert function basically tells the
  // typechecker that you are NOT going to give it a value that doesn't exist
  // in the enum.
  give_shirt(Size::assert("small"));
}

sales();
Output
Hello. We have size small
Here is your shirt of size small

Implicit Casting

By using the as constraint operator, you can make it so casting to the underlying type is implicit.

<?hh

namespace Hack\UserDocumentation\Enums\Intro\Examples\ImplicitCasting;

// We are now telling the typechecker that this enum is constrained to the
// underlying string type, and that implicit casts should be allowed.
enum Size : string as string {
  SMALL = "small" ;
  MEDIUM = "medium";
  LARGE = "large";
  X_LARGE = "x-large";
}

function say_greeting_with_size(string $size): void {
  echo "Hello. We have size " . $size . PHP_EOL;
}

function give_shirt(Size $size): void {
  echo "Here is your shirt of size " . $size . PHP_EOL;
}

function sales(): void {
  // Implict casting at work
  say_greeting_with_size(Size::SMALL);
  // No implicit casting this way though.
  // To go from the underlying type to the enum type, Hack provides builtin
  // enum functions to help you. The assert function basically tells the
  // typechecker that you are NOT going to give it a value that doesn't exist
  // in the enum.
  give_shirt(Size::assert("small"));
}

sales();
Output
Hello. We have size small
Here is your shirt of size small