feat: multiple return values — bare-paren signatures, named returns, must-set, defaults
A function may return multiple values via a bare-paren return signature: `-> (A, B)` / `-> (x: A, y: B)` / `-> (A, B, !)` (error always the last slot), and `-> ()` is `void`. This is DISTINCT from a `Tuple(…)` value — return-position only (a dedicated `ReturnTypeExpr` AST node resolving to a reused `.tuple` TypeId); a parameter / field / variable annotation `x: (A, B)` is rejected. A single-value `-> (T, !)` stays a plain failable (= `-> T !`). Returns use the bare comma form `return a, b` / `return x = a, y = b` (no `.( … )` literal). Consume by destructuring (`a, b := f()`) or single-bind + field access (`c := f(); c.sum`); a failable bound value holds only the value slots (the error stays on the `!` channel). Named return slots are in-scope assignable locals; with no explicit `return` the implicit return is synthesized from them. Path-sensitive definite-assignment enforces the must-set rule, and a slot may carry a default that exempts it. Validation rejects arity mismatches, out-of-slot-order named elements, a slot/parameter name collision, a comma list from a single-value function, and a multi-return signature used as a value type. Examples 0202-0213; readme + specs updated. issues/0197 files a pre-existing annotated-assignment type-check gap (`x: i32 = "hi"` segfaults) surfaced by the adversarial review.
This commit is contained in:
53
readme.md
53
readme.md
@@ -132,6 +132,59 @@ v : `i2 = ---; // referenced as a type
|
||||
x : i2 = 3; // bare `i2` in type position is still the int type
|
||||
```
|
||||
|
||||
### Multiple return values
|
||||
|
||||
A function can return several values with a bare-paren return signature —
|
||||
positional `-> (A, B)` or named `-> (x: A, y: B)`. The empty `-> ()` is `void`,
|
||||
and a trailing `!` is the error channel (always the last slot): `-> (A, B, !)`. A
|
||||
multi-return is **not** a tuple value — it is a distinct return shape (so a
|
||||
parameter / field / variable annotation `x: (A, B)` is rejected; use `Tuple(…)`
|
||||
for an actual tuple value).
|
||||
|
||||
```sx
|
||||
divmod :: (a: i64, b: i64) -> (i64, i64) {
|
||||
return a / b, a % b; // bare comma return — no `.( … )` literal
|
||||
}
|
||||
|
||||
stats :: (a: i32, b: i32) -> (sum: i32, big: bool) {
|
||||
return sum = a + b, big = a > b; // named, in slot order
|
||||
}
|
||||
```
|
||||
|
||||
Consume the result by **destructuring** or by binding it once and reaching the
|
||||
value slots by **field**:
|
||||
|
||||
```sx
|
||||
q, r := divmod(17, 5); // q = 3, r = 2
|
||||
c := stats(40, 2); // c.sum = 42, c.big = true
|
||||
```
|
||||
|
||||
For a **failable** multi-return, the error rides the separate `!` channel — a
|
||||
bound value holds only the value slots, never the error:
|
||||
|
||||
```sx
|
||||
classify :: (n: i32) -> (doubled: i32, big: bool, !) {
|
||||
if n < 0 { raise error.Bad; }
|
||||
return doubled = n * 2, big = n > 10;
|
||||
}
|
||||
d, b := classify(7) catch (e) { … }; // error stripped by `catch`; d, b are the values
|
||||
```
|
||||
|
||||
**Named returns as locals.** Named slots are in-scope assignable locals; assigning
|
||||
them *is* the return (no explicit `return` needed). A slot may carry a default,
|
||||
which exempts it from the must-set rule:
|
||||
|
||||
```sx
|
||||
combine :: (a: i32, b: i32) -> (sum: i32 = 0, good: bool) {
|
||||
good = a > b;
|
||||
sum = a + b; // both slots set → implicit return
|
||||
}
|
||||
```
|
||||
|
||||
A named slot that is **not assigned on every path** and has no default is a
|
||||
compile error (definite-assignment) — rather than returning an uninitialized
|
||||
value.
|
||||
|
||||
### Structs
|
||||
|
||||
```sx
|
||||
|
||||
Reference in New Issue
Block a user