Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Constants and Initializers

A namespace constant binds one name to one value:

const ANSWER = 42;
const LABEL = 'answer';

assert!(ANSWER == 42);

Namespace constants have no written type. Their value gives them a type. A class-like constant may state a type, as the class chapter shows.

Constant expressions

Constants, attribute arguments, parameter defaults, and property defaults use constant expressions. Such an expression may use:

  • scalar literals and existing constants;
  • file!() and directory!();
  • the platform information constructs;
  • embed! with a literal relative path;
  • unary and binary operators;
  • conditional expressions whose condition and both branches are constant expressions;
  • tuple, vec, and dict literals, including vec and dict spreads;
  • a fn closure with no captures;
  • a named class construction;
  • function, static method, and method calls whose inputs are constant expressions.

Calls may run code, inspect state, and throw. “Constant expression” names the source forms allowed at the use site. It does not mean pure or compile-time work. The expression itself cannot read a local variable or $this directly.

fresh!() cannot appear in a constant expression. An indirect call that reaches it during an initializer throws TypeError. Create and store a fresh value in ordinary runtime code instead.

final class Box {
  public function __construct(public int $value) {}

  public static function from(int $value): Box {
    return new Box($value);
  }
}

function add(int $left, int $right): int {
  return $left + $right;
}

const TOTAL = add(20, 22);
const BOX = Box::from(TOTAL);

assert!(BOX->value == 42);

A fn closure may use an expression or block body. Its parameters and locals are not captures. The compiler rejects closures that need outer variables or $this:

const DOUBLE = fn(int $value): int => $value * 2;
const MAKE_READER = fn(int $value): fn(): int {
  return fn(): int => $value;
};

assert!((DOUBLE)(21) == 42);
assert!((MAKE_READER)(42)() == 42);

MAKE_READER captures nothing. When called, it creates a closure that captures the supplied $value.

Forms that do not qualify

A constant expression cannot use:

  • a variable or $this;
  • assignment, indexing, or a property read;
  • interpolation;
  • a closure with captures;
  • match, throw, or a partial call;
  • language constructs other than those listed above;
  • vec[$value; $size];
  • a class name held in an expression.

The compiler reports which form broke the rule.

When Whim evaluates values

Whim evaluates a namespace or class constant when it declares that symbol. It evaluates a static property default when it declares the class.

Whim evaluates an instance property default for each new object. Two objects do not share an object made by that default. It evaluates an omitted parameter default for each call.

An attribute argument runs when Whim creates the attribute value for its target.

Self-reference and load order

A constant may refer to a constant declared later in the same compiled unit. Whim resolves the name before it starts the unit. A constant cannot depend on itself, whether the path is direct or passes through other constants.

Code may use a loaded constant from another unit. An unknown constant may run the autoloader. A failed load leaves the constant undefined.