RFC dan proposal mencatat rancangan. Untuk kemampuan yang benar-benar dapat dipakai, lihat matriks kapabilitas runtime dan catatan rilis.
Draft RFC 0009: Small syntax and semantics decisions
Status: Proposed — draft for review by the project owner. Not accepted; nothing in this document changes implementation status. The exact numbers, examples, and diagnostics text are subject to revision before acceptance. Written to be folded into docs/rfcs/ after review.
Depends on: RFC 0001 (core types and errors), RFC 0007 (diagnostics). All four decisions below are intentionally small, independently shelvable, and motivated by concrete friction in the current language slice.
Decision 1 — Assignment surface: let / var, retiring set
Problem. Three keywords cover one concept. A var value needs set result = result + value to mutate (examples/functions.yz); beginners type result = ... instead, and the compiler rejects it with no elegant migration path. let/var distinguish mutability, which is worth keeping; set only distinguishes assignment instruction from declaration, which most languages handle with a bare =.
Decision (proposed).
let name: T = exprdeclares an immutable local — unchanged.var name: T = exprdeclares a mutable local — unchanged.- A bare assignment
name = exprinside a scope wherenameis a visiblevarbecomes legal and means the same asset name = expr. Same for field updates (value.field = expr). setremains valid with identical semantics, initially non-deprecated; after the language specification exists (RFC 0007 normative docs), it is moved to a documented deprecation cycle: warn, then remove in the next major.- Assignment is an expression-valued statement, not an expression: it does not participate in function call chains (this preserves the current no-arbitrary-nesting stance of
?and match).
# Before
var total: Number = 0
set total = total + current
# After (proposed)
var total: Number = 0
total = total + current
Staged implementation. Add bare name = and name.field = assignment parsing for visible vars (negative tests: assignment to a let binding must still fail, assignment to an undeclared name must still fail, assignment to a shape field of an immutable value must still fail). set keeps compiling unchanged. Compiler diagnostics for both forms cite the same error message text.
Acceptance criteria. Existing set-using tests keep passing unchanged; a new conformance test exercises bare assignment through nested if/while blocks; both ASan/UBSan and self-host byte-identical runs pass after every change.
Open question. Should set warn or fail if it targets a let? Proposed: keep the current failure with an improved message that mentions the two alternatives.
Decision 2 — Named shape constructors
Problem. Constructors are positional and must list fields in declaration order: Product(7, "YAZA Language Guide", int32_from_number(1499), true) (examples/shapes.yz). Swapping two same-typed fields compiles silently. For data-carrying API shapes this is a recurring real defect.
Decision (proposed).
- Named form:
Product(id: 7, name: "N", price: int32_from_number(1499), available: true)— order-independent, one value per field, exactly all fields (no partial construction). - Positional form remains legal. It is not deprecated now because it is concise for small/interchangeable shapes; a later deprecation can follow once the spec exists.
- Mixing named and positional in one constructor is a compile error.
- Duplicate field name, unknown field name, and a missing field are compile errors naming the shape and field.
Compiler note. Field names are already known from shape records (compiler/native_cli.yz); the parser can accept identifier ':' elements and resolve them against native_shape_field_* records, so this requires no new type machinery.
Acceptance criteria. Positive: named construction, reordered fields, use in Option[Z]/Result payloads and function returns. Negative: duplicate, unknown, missing field, mixed form, and a swapped-position case that previously would have compiled (now caught by the named form's distinctness).
Decision 3 — Omitting the fallback return after an exhaustive match
Implementation note (2026-10-27). The native compiler now accepts a non-Unit function whose final supported if/elif/else chain or exhaustive statement-form match returns from every branch, so an unreachable fallback is no longer needed for those cases. Sequential early returns followed by a fallback also work. This resolves the motivating behavior for the currently supported control-flow forms; the draft remains unaccepted and is not a promise of general control-flow-graph analysis. set label = ... remains valid for assigning to an existing mutable var; the separate proposal to retire set in Decision 1 has not been accepted or implemented.
Original problem. When this draft was written, a function needed a top-level fallback return even when its final statement was an exhaustive match (examples/variants.yz); examples used a dead return "unreachable". The implementation note above records that the compiler now handles the supported exhaustive match and conditional cases without that fallback.
Decision (proposed).
- A
matchthat covers every declared case exactly once and whose every arm ends inreturn(or arms omitted when the function returns a non-Unitvalue are illegal) satisfies the function's return obligation; the fallback top-levelreturnis no longer required after it. - The function's declared return type must equal the type every arm's
returnproduces; the compiler already checks each arm, so the new check compares the existing per-arm results against the signature. - Non-exhaustive
matchstill requires the fallback return (or the missing arm is a compile error, as today). Option/Resultblock matches (some/none,ok/err) inherit the same rule.
Acceptance criteria. A variant match with all four arms returning compiles without a trailing return; deleting one arm without restoring the fallback is rejected; identical shape mutates nothing about ergonomics for while/for returning.
Decision 4 — One consistent text-conversion rule
Problem. say renamed.available prints a Boolean directly, while Int32 needs int32_text(...) (examples/shapes.yz). The interpolation path {value} and the concatenation path already disagree, and the tutorial has to teach exceptions. One rule that beginners can carry will do more for consistency than any new keyword.
Decision (proposed). Adopt a single rule: every primitive type has exactly one canonical text form, and say/interpolation apply it uniformly: Text unchanged, Boolean → true/false, Number/Int32 → decimal, Float64 → the existing shortest round-trip decimal form, Bytes → unchanged requirement of an explicit decoder (binary stays binary). String concatenation via + follows the same rule, replacing today's helper-only conversions for Number/Int32/Float64/Boolean.
Migration. The existing helpers stay as named functions (they are often useful for explicitness); the new rule only adds implicit conversion where +/say/{} previously rejected the type. A separate negative test locks the Bytes boundary so binary data cannot silently become garbage text.
Acceptance criteria. Concatenating and interpolating each primitive matches the table above; a Bytes-in-+ rejection test passes; generated output for the existing examples does not change for the types that already converted.
Why these four together
All four attacks the same axis — the language's beginner-facing story — without expanding the type system, without new syntax volume, and all are independently shippable and testable against the compiler's existing record-based structure. They are priority-orderable against Gate A work because none of them blocks it; recommendation is to schedule them after Gate A Stage 1 stabilizes the dispatch model, so the language spec that RFC 0007 requires can reflect the final shapes.
Open questions
- Does
setretirement happen conditionally on the normative spec landing, or at a fixed milestone? - Should named constructor fields allow trailing
_for partial fields (none is proposed today)? - Should the exhaustive-match rule extend to
matchused as the final statement of awhilebody (currently illegal toreturnfrom inside a loop unless matched by a top-level fallback)?