Typed classical values
Design document · Original source
RFCs record designs and changes. A proposal appearing here does not mean its feature is ready to use. Explore current language support
Status recorded in the original: Runtime experimental
Bound original source · SHA-2568c5811cb9f44707de4bee907fbc40c42fd27f69e14504fdef008030fff0b9cc4
- Status: runtime experimental
- Contract:
0.2.0-runtime-experimental - Feature flag:
experimental.typedClassicalValues=true - Stable 0.1 default: disabled
- Owners: language and runtime
Problem
N/M 0.1 has a useful but deliberately narrow classical surface: a measurement can bind one Bit, Bit expressions can use not/and/xor/or, and quantum control can branch on those bits. const accepts Int and Float labels, but those labels do not form a runtime type system. A program cannot safely aggregate multiple measured bits, declare a fixed classical vector, or make a conversion from the quantum measurement boundary explicit.
This RFC adds a bounded immutable value layer without turning the browser runtime into a general-purpose classical machine.
Proposed syntax
module typed_measurement_summary;
@seed(17);
fn main() {
let q: QReg<3> = qreg[3];
H(q[0]);
CNOT(q[0], q[1]);
let a: Bit = measure(q[0]);
let b: Bit = measure(q[1]);
let c: Bit = measure(q[2]);
let outcomes: Array<Bit, 3> = [a, b, c];
let encoded: Int = bits_to_int(outcomes);
let ones: Int = count_ones(outcomes);
let ratio: Float = int_to_float(ones) / 3.0;
let weights: Array<Float, 3> = [1.0, 2.0, 3.0];
let average: Float = mean(weights);
return encoded;
}Declarations are immutable and require an explicit type:
Int: a signed JavaScript-safe integer;Float: a finite number with absolute value at most1_000_000_000_000;Array<Bit, N>,Array<Int, N>, orArray<Float, N>: an immutable fixed-length array where1 <= N <= 256.
Array indexing uses a compile-time integer literal and must remain inside the declared bound. Nested arrays, mutation, dynamic indexing, implicit numeric widening, and user-defined classical functions are outside the first contract.
Expressions and conversions
Int and Float expressions support parentheses, unary minus, and +, -, *, /. Remainder % is restricted to two Int operands. Division always returns Float. Division by zero, non-finite results, unsafe integers, and values outside the runtime bound fail closed.
There is no implicit conversion across the measurement boundary or from Int to Float. The first helper set is deliberately small:
| Helper | Input | Output | Contract |
|---|---|---|---|
bit_to_int | Bit | Int | exact 0 or 1 conversion |
int_to_float | Int | Float | exact numeric widening |
bits_to_int | Array<Bit, N> | Int | most-significant bit first, N <= 52 |
count_ones | Array<Bit, N> | Int | population count |
mean | Array<Int, N> or Array<Float, N> | Float | finite arithmetic mean |
The explicit helpers make it visible when a sampled quantum result becomes a wider classical number. They also prevent a Bit from silently participating in arithmetic.
AST, runtime, and evidence
NMClassicalDeclaration records the declared scalar or array type and a structured expression tree. NMProgram.typedClassicalValues records explicit negotiation of this RFC. The canonical printer preserves types, fixed lengths, helpers, indexing, and arithmetic grouping.
Runtime evaluation is deterministic after the underlying measurements have been sampled. Every successfully evaluated declaration appends ordered evidence with its name, canonical type, value, and source line. Core and Worker must return byte-equivalent evidence for the same seed and source.
The runtime never substitutes a default for a missing binding, invalid index, division by zero, non-finite value, unsafe integer, mixed array, or type mismatch. It emits NM-RUNTIME-032 and stops the run.
Diagnostics
| Code | Meaning |
|---|---|
NM-PARSE-062 | Typed classical syntax used without explicit feature negotiation |
NM-PARSE-063 | Malformed declaration, type, expression, helper, or array bound |
NM-TYPE-023 | Declared and inferred types do not match |
NM-TYPE-024 | Unknown/duplicate value, invalid bound, or invalid helper argument |
NM-RUNTIME-032 | Runtime value violated the bounded typed-classical contract |
Compatibility and rollout
- Stable N/M 0.1 parsing and execution remain unchanged.
- Existing
Bitmeasurement and Boolean expression syntax remains the source of quantum-to-classical values. - Existing
const name: Int|Float = ...declarations remain source-compatible. When this RFC is enabled they also participate in type checking, so anIntconstant must actually be an integer. - Feature negotiation must be explicit in Core callers, CLI, Language Server, VS Code, Worker, and browser surfaces.
- Promotion requires AST/printer/runtime conformance, Core/Worker evidence parity, CLI/LSP surface parity, packed-package validation, maximum-bound performance evidence, EN/TR documentation, and current-candidate Windows/Linux Node 20/22 CI evidence.
Non-goals
This RFC does not add mutable variables, array writes, strings, maps, structs, dynamic allocation, arbitrary loops, dynamic indexes, nested arrays, implicit casts, arbitrary classical function calls, branch-sensitive definite-assignment inference, cryptographic bit strings, or host-language interop. A future typed-result RFC may allow these values to become a structured function result; this contract only makes declaration, safe aggregation, runtime evidence, and existing identifier returns explicit.