Built In Types: Nothing
The type nothing
is the bottom type in the Hack typesystem. This means that there is no way to create a value of the type nothing
. nothing
only exists in the typesystem, not in the runtime.
The concept of a bottom type is quite difficult to grasp, so I'll first compare it to the supertype of everything mixed
. mixed
is the most general thing you can imagine within the hack typesystem. Everything "extends" mixed
if you will. nothing
is the exact opposite of that.
Let's work out the hierarchy of scalar types. Forget about nullable types and dynamic
for the moment, they would make this example far more complex without adding much value.
mixed
is at the top. Everything is a subtype ofmixed
, either directly (types that have no other supertypes) or indirectly (via their supertypes).num
is a subtype ofmixed
.arraykey
is a subtype ofmixed
.bool
is a subtype ofmixed
.int
is a subtype ofnum
andarraykey
.float
is a subtype ofnum
.string
is a subtype ofarraykey
.nothing
is a subtype ofint
,float
,string
, andbool
.
The important thing to note here is that nothing
is never inbetween two types. nothing
only shows up right below a type with no other subtypes.
Okay that was all very academical, but "what can I use it for?", I hear you ask.
When making a new / empty Container<T>
, Hack will infer its type to be Container<nothing>
. It is not that there are actual value of type nothing
in the Container<T>
, it is just that this is a very nice way of modeling empty Container<T>
s.
Should you be able to pass an empty vec where a vec<string>
is expected? Yes, there is no element inside that is not a string
, so that shoud be fine. You can even pass the same vec into a function that takes a vec<bool>
since there are no elements that are not of type bool
. What are you allowed to do with the $nothing
of this foreach? Well, you can do anything to it. Since nothing is a subtype of everything, you can pass it to any method and do all the things you want to.
function takes_vec_of_strings(vec<string> $_): void {}
function takes_vec_of_bools(vec<bool> $_): void {}
<<__EntryPoint>>
async function main_async(): Awaitable<void> {
$empty_vec = vec[];
takes_vec_of_bools($empty_vec);
takes_vec_of_strings($empty_vec);
foreach ($empty_vec as $nothing) {
$nothing->whatever();
takes_vec_of_strings($nothing);
}
}
To make an interface that requires that you implement a method, without saying anything about its types. This does still make a requirement about the amount of parameters that are required parameters.
interface DontForgetToImplementShipIt {
public function shipIt(nothing $_): mixed;
}
abstract class Software implements DontForgetToImplementShipIt {
}
class HHVM extends Software {
public function shipIt(string $version): string {
return 'Shipping HHVM version '.$version.'!';
}
}
class HSL extends Software {
private function __construct(public bool $has_new_functions) {
}
public function shipIt(bool $has_new_functions): HSL {
return new HSL($has_new_functions);
}
}
class HHAST extends Software {
public function shipIt(Container<string> $linters): void {
foreach ($linters as $linter) {
invariant(
Str\ends_with($linter, 'Linter'),
'Linter %s does not have a name that ends in "Linter"!',
$linter,
);
}
}
}
It is important to note that Software::shipIt()
is not directly callable without knowing what subtype of Software
you have.