Contexts And Capabilities: Contexts And Subtyping
Note: Context and capabilities are enabled by default since HHVM 4.93.
Capabilities are contravariant.
This implies that a closure that requires a set of capabilities Sa may be passed where the expected type is a function that requires Sb as long as Sa ⊆ Sb.
For the following example, assume that the default context includes at least {Rand, IO}
function requires_rand_io_arg((function()[rand, io]: void) $f): void {
$f();
}
function caller(): void {
// passing a function that requires fewer capabilities
requires_rand_io_arg(()[rand] ==> {/* some fn body */});
// passing a function that requires no capabilities
requires_rand_io_arg(()[] ==> {/* some fn body */});
}
Additionally, this has the standard implication on inheritance hierarchies. Note that if Sa ⊆ Sb it is the case that Sb is a subtype of Sa.
For the following, assume the default set contains {IO, Rand, Throws
class Parent_ {
public function maybeRand()[rand]: void {/* some fn body */} // {Rand}
public function maybePure(): void {/* some fn body */} // {Throws<mixed>, IO, Rand}
}
class Mid extends Parent_ {
public function maybeRand()[rand]: void {/* some fn body */} // {Rand} -> fine {Rand} ⊆ {Rand}
public function maybePure()[io]: void {/* some fn body */} // {IO} -> fine {IO} ⊆ {Throws<mixed>, IO, Rand}
}
class Child extends Mid {
public function maybeRand()[]: void {/* some fn body */} // {} -> fine {} ⊆ {Rand}
public function maybePure()[]: void {/* some fn body */} // {} -> fine {} ⊆ {IO}
}
Capability subtyping
In reality, there may also exist a subtyping relationship between capabilities. Thus, whenever some capability B that is subtype of capability A is available, any function or operation that requires A may be called or performed, respectively. This works identically to standard type subtyping.
For the following, assume that the following contexts and capabilities exist: Rand, ReadFile <: Rand, rand: {Rand}, readfile: {Readfile}
function requires_rand()[rand]: void {/* some fn body */}
function has_readfile()[readfile]: void {
requires_rand(); // fine {readfile} ⊆ {Rand} since readfile <: rand
}