Automatic boxing

Zena has no automatic boxing. A primitive is never silently wrapped in a heap object to make it fit somewhere a reference is expected. Where a boxed primitive is needed, Box<T> is written explicitly:

zena
let x: Box<i32> = new Box(42); // the allocation is visible

Why it is rejected ​

Boxing is an allocation. Zena's rule is that an allocation is visible in the source that causes it, and automatic boxing breaks that rule in the worst place: inside loops, per element, in code that reads as if it does nothing.

The rule is not an isolated preference. Three other decisions exist to enforce it:

  • Unions cannot mix primitives with references, so i32 | String is rejected rather than boxed.
  • Generics are reified, so Array<i32> holds unboxed i32 rather than boxed elements.
  • Multi-value returns exist so that an optional result does not need an allocated Option.

Automatic boxing would have undone all three.

No any ​

Zena has no any type. A type that accepts every value has to do one of two things, and neither is acceptable:

  • Waive type checking, as TypeScript's any does. Zena's type system is sound: a program that type-checks has correct types at runtime. A construct that opts out of checking gives that up wherever it appears.
  • Box primitives so they fit alongside references. That is the allocation this page exists to rule out.

A checked any — one that requires an explicit cast before use — solves the first problem and not the second. It still allocates on let x: any = 42, with nothing in the line to say so.

anyref is a different type ​

anyref is the top type for reference types. Every class, array, and closure is an anyref; no primitive is.

zena
let r: anyref = someObject; // no allocation — already a reference
let n: anyref = 42; // error

It is the version of "could be anything" that costs nothing, and it covers the case an any type would be reached for.

→ Working documents: primitive-boxing-semantic-types.md, polymorphic-references.md