Readonly: Introduction

readonly is a keyword used to create immutable references to Objects and their properties.

How does it work?

Expressions in Hack can be annotated with the readonly keyword. When an object or reference is readonly, there are two main constraints on it:

  • Readonlyness: Object properties cannot be modified (i.e. mutated).
  • Deepness: All nested properties of a readonly value are readonly.


Object properties of readonly values cannot be modified (i.e. mutated).

class Bar {
  public function __construct(
    public Foo $foo,
class Foo {
  public function __construct(
    public int $prop,
  ) {}

function test(readonly Foo $x) : void {
  $x->prop = 4; // error, $x is readonly, its properties cannot be modified
Typechecker output
File "/home/example/readonly.readonlyness.hack", line 18, characters 3-14:
This expression is readonly, its members cannot be modified (Parsing[1002])


All nested properties of readonly objects are readonly.

function test(readonly Bar $x) : void {
  $foo = $x->foo;
  $foo->prop = 3; // error, $foo is readonly
Typechecker output
File "/home/example/readonly.deepness.hack", line 8, characters 3-16:
This expression is readonly, its members cannot be modified (Parsing[1002])

How is readonly different from contexts and capabilities that control property mutation (such as write_props)?

Contexts such as write_props affect an entire function (and all of its callees), whereas readonly affects specific values / expressions.

Topics covered in this section