Proposal: tomb β€” Irreversible Variable Finalization

Proposal: tomb β€” Irreversible Variable Finalization

Author: Jefferson
Status: Idea / Stage 0 Discussion


Summary

This proposal introduces a new JavaScript keyword:

tomb variable;

The purpose of tomb is to permanently transition a mutable binding (let) into an immutable one during program execution.

Unlike const, which requires immutability at declaration time, tomb allows a variable to remain mutable while it is being initialized, discovered, or constructed, and then permanently finalizes the binding once its value is considered complete.

The operation is irreversible.


Design Goals

This proposal aims to:

  • Introduce a third binding state between let and const.
  • Preserve JavaScript's existing initialization patterns.
  • Express variable lifecycle explicitly.
  • Improve code readability by making finalization explicit.
  • Enable future engine optimizations through irreversible binding finalization.

Motivation

JavaScript currently provides two choices:

const value = ...

or

let value = ...

There is no native mechanism that expresses:

"This variable needed to be mutable while obtaining its value, but from this point forward it must never change."

Developers commonly solve this by introducing additional variables.

Example:

let user = await fetchUser();

const finalUser = user;

With tomb:

let user = await fetchUser();

tomb user;

The programmer's intent becomes explicit.


Proposed Syntax

Preferred syntax:

tomb user;

Alternative syntax:

tomb(user);

The keyword form is preferred because it represents a language statement rather than a function.


Semantics

When executed:

tomb user;

The binding immediately becomes immutable.

Subsequent assignments throw a TypeError.

user = other;
// TypeError

The operation cannot be reversed.


Formal State Transition

Declaration
      β”‚
      β–Ό
Mutable Binding (let)
      β”‚
      β”‚ assignments allowed
      β–Ό
tomb
      β”‚
      β–Ό
Finalized Binding
      β”‚
      └── further assignment β†’ TypeError

Rationale

Unlike const, which defines immutability at declaration, tomb defines immutability at a chosen point in the variable lifecycle.

This proposal introduces a third conceptual binding state:

Mutable
    ↓
tomb
    ↓
Finalized

This is not merely syntactic sugar over const; it represents an explicit state transition during execution.


Examples

Example 1 β€” Multiple lookup sources

let user = await findByEmail(email);

if (!user)
    user = await findByUsername(username);

tomb user;

Example 2 β€” Configuration loading

let config = await loadConfig();

tomb config;

Example 3 β€” Authentication

let session = await restoreSession();

if (!session)
    session = await login();

tomb session;

Example 4 β€” Cache lookup

let data = cache.get(id);

if (!data)
    data = await database.find(id);

tomb data;

Example 5 β€” Builder Pattern

let request = new RequestBuilder();

request.url("/login");
request.method("POST");

tomb request;

Example 6 β€” Dependency Injection

let container = createContainer();

container.register(ServiceA);
container.register(ServiceB);

tomb container;

Example 7 β€” Environment detection

let runtime = detectRuntime();

tomb runtime;

Example 8 β€” Feature detection

let supportsSIMD = detectSIMD();

tomb supportsSIMD;

Example 9 β€” Application startup

let app = await initialize();

tomb app;

Example 10 β€” Compiler pipeline

let ast = parse(source);

optimize(ast);

tomb ast;

generate(ast);

Advantages

Expressive lifecycle

A variable can evolve naturally.

let
 ↓
mutation
 ↓
tomb
 ↓
finalized

Self-documenting

When reading:

tomb config;

the programmer immediately understands:

This value is finalized and must never be reassigned.


Explicit lifecycle transition

Rather than deciding mutability only at declaration time, the programmer explicitly indicates when a value has reached its definitive state.


Potential engine optimizations

Once a binding becomes finalized, engines may apply optimizations similar to those available for const.

Possible optimizations include:

  • Constant propagation
  • Dead assignment elimination
  • Reduced write checks
  • Register promotion

Difference from const

const

  • Immutable from declaration.
  • Must be initialized immediately.
const value = load();

tomb

  • Mutable while being initialized.
  • Finalized afterwards.
let value;

value = load();

tomb value;

Difference from Object.freeze()

Object.freeze() freezes an object's properties.

tomb freezes the variable binding.

let obj = {};

Object.freeze(obj);

The variable itself is still mutable:

obj = {};

With:

tomb obj;

The binding cannot change.

This proposal intentionally affects only the binding, not the referenced object.


Why not just use const?

const requires the developer to know that a binding is immutable at declaration time.

This proposal addresses a different situation:

The final value is not known when the variable is declared.

The variable must remain mutable while its value is being determined, and only later transition into an immutable state.

This proposal introduces a new concept:

Binding finalization during execution

rather than declaration-time immutability.


Open Questions

Should it work only on let?

Proposed answer:

Yes.

Applying tomb to const should be a syntax error.


Should it work on var?

Probably not.

var has legacy semantics and lacks block scoping.


Should multiple tomb operations be allowed?

tomb user;
tomb user;

Possible answers:

  • Allowed (no-op)
  • Throw TypeError

Closures

let value = 1;

function increment() {
    value++;
}

tomb value;

increment();

Should this throw?


Destructuring

let { a, b } = obj;

tomb a;

Should individual bindings be supported?


Loop variables

for (let item of list) {
    tomb item;
}

Should this be legal?


Non-goals

tomb is not intended to:

  • Freeze object properties.
  • Replace Object.freeze().
  • Replace const.
  • Replace immutable data libraries.

Its sole purpose is to express a transition from a mutable binding to a finalized binding.


Possible Future Extensions

If accepted, future discussions might consider:

  • tomb for function parameters.
  • tomb inside pattern matching.
  • Compiler optimizations.
  • Optional deep-freeze semantics.

Naming

The keyword name used in this proposal (tomb) is provisional and intended only to describe the concept.

The primary goal of this proposal is to discuss the language feature, not the final keyword.

Alternative names may be more appropriate if the proposal evolves.

The name tomb was inspired by the Portuguese word "tombamento", a legal mechanism used in Brazil to protect buildings, monuments, and historically significant structures.

Once a property is officially tombada, it becomes protected against demolition or arbitrary modification.

The proposed keyword follows the same conceptual idea:

  • a variable starts mutable;
  • after reaching its final state, it becomes "protected";
  • further reassignment becomes illegal.

The analogy refers only to the concept of preservation and irreversibility.

The final keyword, if this idea were ever adopted, should be determined by the TC39 community through the standard proposal process.


Conclusion

tomb introduces a new concept into JavaScript:

A binding whose mutability changes during its lifecycle.

Instead of forcing developers to choose between const and let at declaration time, tomb allows mutation during construction while preserving immutability after explicit finalization.

Rather than being a convenience feature, tomb introduces an explicit lifecycle transition for variable bindings, improving readability, expressing programmer intent, and potentially enabling future engine optimizations.

Counter intuitively the main JS engines actually don't have many optimisations for const, due to needing to check if it's TDZ. Accessing it can be slower than var.

Engines may also not be able to optimize this.


Does this need dedicated syntax. Why not;

const ast = { ref: parse(input) };
optimize(ast.ref);
Object.freeze(ast); // (tomb)

I see the point, but only based on examples 1, 3, 4. In other cases wouldn’t const and Object.freeze do the job perfectly fine?

Examples 1, 3, and 4, in their current form, could also be rewritten with const and a nullish coalescing operator.

I'm reminded of Write-once const bindings which seems similar.

The motivating example for me would be with try catch. Though do expressions or try (/safe assign) expressions would also work for that case.

What is the problem this is solving that warrants not just new syntax, but an entirely new binding form? Is the cost "i don't have to use two variable names, and end with const finalName = tempName;? That seems like a pretty insignificant effort for such a complex solution.

1 Like