Queue generic structs and array lengths, after the core lane and not beside it
This commit is contained in:
parent
b5d2b5eabd
commit
d66d717716
53
NEXT.md
53
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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user