A type variable is marked, because absence is a bad thing to give meaning to

This commit is contained in:
Joseph Ferano 2026-09-13 13:09:25 +07:00
parent 97cb77d949
commit 7be57efe18

34
NEXT.md
View File

@ -1,3 +1,37 @@
## Decided by the author, 2026-09-13: a type variable takes a `$` sigil
**This overrides plan.org**, which says under Types: *"Lowercase type names are variables, Capitalized are
concrete — no sigil."* That rule is withdrawn. Taken as provisional, to make progress rather than as a final
design, and expected to be revisited after some real use.
```lisp
(defn swap [xs [$t] i i32 j i32] () ...)
```
Three reasons, the third being the one that actually forced it.
1. **There is no binding site without a sigil.** Nothing distinguishes where a variable is introduced from where
it is used.
2. **Introducing one is invisible.** `check.ml:535-562` resolves builtins, aliases, structs, unions, enums, then
near-miss, and *then* treats any leftover lowercase name as a type variable. So a lowercase name nobody has
heard of silently becomes a type parameter, and a mistyped type makes the function **more** permissive instead
of failing. The `near_miss` guard at line 553 exists for exactly this hazard and only reaches names close to a
known one — the comment there says so. `[elem]` is not close to anything and would become generic silently.
3. **Brackets carry two opposite rules for the same lexical thing.** In `[n t]`, `check.ml:563` resolves `n`
against `env.consts` and *errors* if it is absent, while `t` becomes a *variable* if absent. Absence means
"mistake" on one side of the bracket and "new parameter" on the other.
The rule is also not the one plan.org states. It is not case-based: `i32`, `f32`, `bool` and `string` are all
lowercase and concrete. What the code actually does is **treat any unresolved lowercase name as a variable**,
which is an absence rule, and absence is a bad thing to give meaning to.
**Open, and deliberately not decided here:** whether a *use* after the binding is `$t` or bare `t`. Odin binds with
`$T` and uses bare `T`. Left to whoever builds it, to be reported with the reason.
**Also open: `$n` in length position**, Odin's `$N: int`, which is what would make a function generic over array
length rather than only element type. Not in the spike unless it falls out for free; the spike is to report what it
would cost.
## To discuss: five gaps the raylib examples hit and could not close
Found by the lane that ported `core-2d-camera`, `core-scissor-test`, `core-window-flags`,