diff --git a/NEXT.md b/NEXT.md index 2b67a48..197ffe5 100644 --- a/NEXT.md +++ b/NEXT.md @@ -1,3 +1,56 @@ +## Queued, decided by the author: generic structs and generic array lengths + +**Sequenced after the core generics lane, not beside it.** Both change `Types.t`, which that lane is inside. + +The author's question settled it: Odin has both, and the normal way to write them is together. +`core/math/linalg/extended.odin:488` binds three things in one pattern — + +```odin +is_nan_array :: proc(x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) +``` + +`A` is the array type, `N` its length, `T` its element, and `[N]bool` in the *return* type uses `N`, so the result +is an array of the same length as the argument. And `core/container/small_array/small_array.odin:23` is the generic +struct over both, with a predicate over a **value** rather than a type: + +```odin +Small_Array :: struct($N: int, $T: typeid) where N >= 0 { ... } +``` + +### Why they are one project + +`Types.Named` is a bare string with no parameters and `Types.Array` is `int64 * t`. Either feature means `Types.t` +gains a parameterised case, and `Types.t` is consumed by `emit.ml`'s layout calculator, `x86.ml`, `render.ml`, the +DWARF path and the map key-pair emitter. **Same price for one as for both**, which is the whole reason to do them +together. + +What the spike measured for the length half specifically: `Ast.len` is `Lint | Lname`, so `$n` parses today with no +reader or parser change — the same free ride the type sigil got. Everything after that is the expensive part. +`bind_ty` gains a length-binding case (`Array (n, p)` against `Array (m, a)` binds `n := m`), `mangle_ty` gains a +number, and `array_len` has to answer "a variable" rather than failing — which means the checker's compile-time +constant folding has to accept that a length can stay symbolic until instantiation. + +### The motivating case, and it is not `$n` on its own + +`$n` alone buys little. `swap!` does not need it — it takes a slice and two indices, and the length is a runtime +field. Nor does a `pop` from a `Vec`. The case that wants both is **a fixed-capacity array with a count and no +allocation**, which is Odin's `Small_Array` and a good fit for a game that refuses to allocate in a frame: + +```lisp +(defvar enemies (Small-Array 64 Enemy)) +``` + +`PORTING.md`'s recommendation for the game's state was fixed arrays with counts held in `defvar` globals, kept +deliberately away from `Vec` so nothing is move-only. `(Small-Array $n $t)` is that pattern with a type behind it +instead of two variables kept in step by hand. That is the argument for building this, and it is stronger than +"Odin has it". + +### Also worth deciding when it is built + +Odin's `where N >= 0` is a predicate over a **value**, not a type. The `where` mechanism just decided here takes +type predicates (`ordered?`, `copyable?`). Whether it also takes value predicates over a length parameter is a +separate question and should be answered deliberately rather than falling out of the implementation. + ## To discuss: two decisions parked during the ownership discussion Both came out of the lane that closed the enum-layout and constant-checking gaps. Its work is sound and committed;