feat(lang): functions are values (L2), and render3d takes its scene as callbacks

fn(int, float) -> bool is a type, fn name is any top-level function's value, and a call through
a local, a global, a record field, a slice element, a parameter or a result of a function type
is an indirect call; two function types mix only when equal, a call checks its argument count,
and a value may be null (examples/functions/values.ludic). Job.parallel_for keeps its worker
check.

render3d's scene is registered rather than required by name: r3d_on_draw, r3d_on_casters and
r3d_on_stream_fill (hooks.ludic). The two rendering examples register theirs - and had defined
scene_draw_casters with no parameter while the renderer passed one, which nothing checked.
render3d declares numbers float itself; smooth.ludic is converted to floats and returns when
r3d_init fails instead of running on into a segfault. Noise.* check their argument count (a call
one short crashed the compiler). selfhost-build says why it failed. The migration tool reads a
declared float as evidence. Seed regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-24 00:27:26 +03:00
parent dc75a5a5ab
commit 0c73287e35
23 changed files with 71430 additions and 68575 deletions

View file

@ -815,6 +815,20 @@ fields: `toks[i].kind = T_ID` is a single address computation.
## Functions & FFI
**Functions are values.** `fn(int, float) -> bool` is a type (no `->` means it returns
nothing), `fn name` is any top-level function as a value of its own type, and a call through a
local, a global, a record field, a slice element, a parameter or a result of a function type
calls whatever it holds. Two function types mix only when they are the same, and a value may be
`null`. A registry holds behaviour this way, and a package takes a callback:
```ludic
# doc-check: skip — illustrative
property Kind { name: string = "", use: fn(Thing) -> bool = null }
function kind_def(name: string, use: fn(Thing) -> bool) -> void { ... }
kind_def("bush", fn bush_use)
if kinds[k].use(t) { sound_pickup() }
```
```ludic
function heal(amount: int) -> int { return amount * 2 }
```