Skip to content

Commit 172b2aa

Browse files
author
Vincent
authored
Enum Class Labels documentation (#1122)
* First draft for enum class labels Introducing doc for enum class labels. This is currently an experimental feature which should be released by the end of the year. A label is an alternative way to access an enum class value, remembering which binding was used: `E::valueOf(E#A) = E::A`. * fixing typoes * update hhconfig * Fixing experimental flags and HH namespace (missing backslash) * Removing unstable attribute * Fixing file extension in examples * fix variance of MemberOf * rebase and update files/attributes so it typechecks with old hhvm * adding missing files * feedback from Andrew * fix typo
1 parent 53de7ab commit 172b2aa

11 files changed

+165
-3
lines changed

.hhconfig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,4 @@ check_xhp_attribute=true
99
enable_experimental_tc_features=shape_field_check,sealed_classes,reified_generics
1010
allowed_decl_fixme_codes=1002,2053,4030,4035,4045,4047,4101,4323
1111
allowed_fixme_codes_strict=1002,2011,2049,2050,2053,4005,4006,4026,4027,4030,4035,4045,4047,4051,4053,4057,4063,4064,4067,4101,4104,4106,4107,4108,4110,4112,4128,4135,4165,4188,4193,4240,4251,4281,4297,4314,4323,4324,4347,4371,4390,4401,4417,4423
12-
enable_enum_classes=true
1312
enable_enum_supertyping=true
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// WARNING: Contains some auto-generated boilerplate code, see:
2+
// HHVM\UserDocumentation\MarkdownExt\ExtractedCodeBlocks\FilterBase::addBoilerplate
3+
4+
namespace HHVM\UserDocumentation\Guides\Hack\BuiltInTypes\EnumClassLabel\EnumClassLabel;
5+
6+
<<file:__EnableUnstableFeatures('enum_class_label')>> // temp
7+
8+
function set<T>(\HH\EnumClass\Label<E, T> $label, T $data) : void {
9+
// setting $data into some storage using $label as a key
10+
}
11+
12+
// all these calls are equivalent
13+
function all_the_same(): void {
14+
set(E#A, 42);
15+
set(#A, 42);
16+
set#A(42);
17+
}

build/extracted-examples/guides/hack/11-built-in-types/36-enum-class-label/EnumClassLabel.alt.hack.hhvm.expect

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// WARNING: Contains some auto-generated boilerplate code, see:
2+
// HHVM\UserDocumentation\MarkdownExt\ExtractedCodeBlocks\FilterBase::addBoilerplate
3+
4+
namespace HHVM\UserDocumentation\Guides\Hack\BuiltInTypes\EnumClassLabel\EnumClassLabel;
5+
6+
// We are using int here for readability but it works for any type
7+
enum class E : int {
8+
int A = 42;
9+
int B = 42;
10+
}

build/extracted-examples/guides/hack/11-built-in-types/36-enum-class-label/EnumClassLabel.definition.hack.hhvm.expect

Whitespace-only changes.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// WARNING: Contains some auto-generated boilerplate code, see:
2+
// HHVM\UserDocumentation\MarkdownExt\ExtractedCodeBlocks\FilterBase::addBoilerplate
3+
4+
namespace HHVM\UserDocumentation\Guides\Hack\BuiltInTypes\EnumClassLabel\EnumClassLabel;
5+
6+
<<file:__EnableUnstableFeatures('enum_class_label')>> // temp
7+
8+
function full_print(\HH\EnumClass\Label<E, int> $label) : void {
9+
echo E::nameOf($label) . " ";
10+
echo E::valueOf($label) . "\n";
11+
}
12+
13+
function partial_print(\HH\MemberOf<E, int> $value) : void {
14+
echo $value . "\n";
15+
}

build/extracted-examples/guides/hack/11-built-in-types/36-enum-class-label/EnumClassLabel.example.hack.hhvm.expect

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// WARNING: Contains some auto-generated boilerplate code, see:
2+
// HHVM\UserDocumentation\MarkdownExt\ExtractedCodeBlocks\FilterBase::addBoilerplate
3+
4+
namespace HHVM\UserDocumentation\Guides\Hack\BuiltInTypes\EnumClassLabel\EnumClassLabelIntro;
5+
6+
enum E : int {
7+
A = 42;
8+
B = 42;
9+
}
10+
11+
function f(E $value) : void {
12+
switch($value) {
13+
case E::A: echo "A "; break;
14+
case E::B: echo "B "; break;
15+
}
16+
echo $value . "\n";
17+
}

build/extracted-examples/guides/hack/11-built-in-types/36-enum-class-label/EnumClassLabelIntro.hack.hhvm.expect

Whitespace-only changes.

guides/hack/11-built-in-types/01-introduction.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ Though not built-in as types, other alternatives exist in [Hack Collections](/ha
2121

2222
## Other Built-In Types
2323
Hack has other built-in types too, like:
24-
[enum](/hack/built-in-types/enum) (with [enum class](/hack/built-in-types/enum-class)),
24+
[enum](/hack/built-in-types/enum) (with [enum class](/hack/built-in-types/enum-class) and [enum class labels](/hack/built-in-types/enum-class-label)
25+
),
2526
[shape](/hack/built-in-types/shapes), and
2627
[tuples](/hack/built-in-types/tuples).
2728

@@ -32,4 +33,4 @@ Other types like [noreturn](/hack/built-in-types/noreturn) and [void](/hack/buil
3233
These last few types are special in their utility and/or versatility:
3334
[classname](/hack/built-in-types/classname),
3435
[dynamic](/hack/built-in-types/dynamic), and
35-
[this](/hack/built-in-types/this).
36+
[this](/hack/built-in-types/this).
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Disclaimer
2+
This feature is on its way to be deployed. Most recent HHVM versions no longer need the `EnableUnstableFeatures` file attribute
3+
to support this. More information available at https://github.com/hhvm/user-documentation/pull/1122
4+
5+
## Values v. Bindings
6+
7+
With [enum types](/hack/built-in-types/enum) and [enum classes](/hack/built-in-types/enum-class), most of the focus is given to their values.
8+
Expressions like `E::A` denote the value of `A` in `E`, but the fact that `A` was used to access it is lost.
9+
10+
```EnumClassLabelIntro.hack no-auto-output
11+
enum E : int {
12+
A = 42;
13+
B = 42;
14+
}
15+
16+
function f(E $value) : void {
17+
switch($value) {
18+
case E::A: echo "A "; break;
19+
case E::B: echo "B "; break;
20+
}
21+
echo $value . "\n";
22+
}
23+
```
24+
In this example, both `f(E::A)` and `f(E::B)` will echo `A 42` because `E::A` and `E::B` are effectively the value `42` and nothing more.
25+
26+
## Enum Class Labels
27+
28+
Sometimes, the binding that was used to access a value from an enumeration is as important as the value itself. We might want to know that `A`
29+
was used to access `E::A`. Enum types provides a partial solution to this with the `getNames` static method, but it is only
30+
safe to call if all the values of the enumeration are distinct.
31+
32+
Enum classes provides a way to do this by using the newly introduced *Enum Class Label* expressions. For each value defined in an enum class, a corresponding
33+
label is defined. A label is a handle to access the related value. Think of it as an indirect access. Consider the following example:
34+
35+
```EnumClassLabel.definition.hack no-auto-output
36+
37+
// We are using int here for readability but it works for any type
38+
enum class E : int {
39+
int A = 42;
40+
int B = 42;
41+
}
42+
```
43+
44+
This example defines two constants `E::A: \HH\MemberOf<E, int>` and `E::B: \HH\MemberOf<E, int>`. Enum class labels add more definitions to the mix:
45+
46+
- `E#A: \HH\EnumClass\Label<E, int>` is the label to access `E::A`
47+
- `E#B: \HH\EnumClass\Label<E, int>` is the label to access `E::B`
48+
- `E::nameOf` is a static method expecting a label and returning its string representation: `E::nameOf(E#A) = "A"`
49+
- `E::valueOf` is a static method expecting a label and returning its value: `E::valueOf(E#A) = E::A`
50+
51+
So we can rewrite the earlier example in a more resilient way:
52+
```EnumClassLabel.example.hack no-auto-output
53+
<<file:__EnableUnstableFeatures('enum_class_label')>> // temp
54+
55+
function full_print(\HH\EnumClass\Label<E, int> $label) : void {
56+
echo E::nameOf($label) . " ";
57+
echo E::valueOf($label) . "\n";
58+
}
59+
60+
function partial_print(\HH\MemberOf<E, int> $value) : void {
61+
echo $value . "\n";
62+
}
63+
```
64+
Now, `full_print(E#A)` will echo `A 42` and `full_print(E#B)` will echo `B 42`.
65+
66+
## Full v. Short Labels
67+
68+
We refer to labels like `E#A` as *fully qualified* labels: the programmer wrote the full enum class name.
69+
However there are some situations where Hack can infer the class name; for example,
70+
the previous calls could be written as `full_print(#A)` and `full_print(#B)`, leaving `E` implicit.
71+
This is only allowed when there is enough type information to infer the right enum class name. For example, `$x = #A` is not allowed and will result in a type error.
72+
73+
### Special case of function calls
74+
75+
When the first argument of a function is a label, we provide an alternative notation to call it.
76+
```EnumClassLabel.alt.hack no-auto-output
77+
<<file:__EnableUnstableFeatures('enum_class_label')>> // temp
78+
79+
function set<T>(\HH\EnumClass\Label<E, T> $label, T $data) : void {
80+
// setting $data into some storage using $label as a key
81+
}
82+
83+
// all these calls are equivalent
84+
function all_the_same(): void {
85+
set(E#A, 42);
86+
set(#A, 42);
87+
set#A(42);
88+
}
89+
```
90+
91+
As you can see, the short name can be written *before* the opening parenthesis of the function call. This only works for a single label argument, which must be the first one.
92+
93+
## Known corner cases
94+
95+
### The `#` character is no longer a single-line comment
96+
This feature relies on the fact that Hack and HHVM no longer consider the character `#` as a single-line comment. Please use `//` for such purpose.
97+
98+
### Labels and values cannot be exchanged
99+
If a method is expecting a label, one cannot pass in a value, and vice versa: `full_print(E::A)` will result in a type error and so will `partial_print(E#A)`.
100+
101+
### `MemberOf` is covariant, `Label` is invariant
102+
A label should be considered as a way to attach a type to a binding. Therefore it is invariant: `E#A` is not of type `\HH\EnumClass\Label<E, arraykey>`.
103+
This can be misleading at first, because `\HH\MemberOf` is covariant (it is data after all): `E::A` is of type `\HH\MemberOf<E, arraykey>`.

0 commit comments

Comments
 (0)