From 3afce2aeacfbcced419fe91edaf3d2ba57377ecc Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 12 Sep 2026 09:38:26 +0700 Subject: [PATCH] Never restarting is the thesis; say where the holes in it are The manual described the signature-change refusal as though it were the design. It is not, and session.ml already said so at the refusal: a signature change should make a new version, leave old callers on the old one, and warn at the stale sites. plan.org calls it signature generations and stale-caller warnings, and it is milestone 7's unfinished half. The struct-layout rejection is the decided one and stays. Conflating them made a placeholder look like a rule. --- NEXT.md | 17 ++++ emacs/MANUAL.md | 40 ++++++-- web/index.html | 249 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 288 insertions(+), 18 deletions(-) diff --git a/NEXT.md b/NEXT.md index cbdc75e..4f2b851 100644 --- a/NEXT.md +++ b/NEXT.md @@ -210,6 +210,23 @@ vague intention — if it is listed, someone has already established it is real. ### Unblocked now, and ranked +**0. Signature generations and stale-caller warnings — milestone 7's unfinished half.** +Promoted here on the author's correction, and `session.ml:146` already says the same thing at the refusal itself. A +changed signature is refused today and **that is a placeholder, not the design**. plan.org's open decision #6 says what +should happen: a signature change makes a new version of the function, new callers resolve it, existing callers and any +stored `Fn` value stay safely on the old one, and the session *warns* at each tracked stale caller site. Milestone 7 +names it outright — "signature generations and stale-caller warnings". + +**The thesis of this project is that you never restart the program.** Every refusal that ends in "restart to change it" +is a hole in that, and this is the biggest one. It needs three things that do not exist: function versions, a +trampoline per version, and caller tracking good enough to name the sites. The cell already gives the indirection; what +is missing is that a cell holds one bare pointer with no signature, so there is nowhere to put a second version. + +A changed **struct layout** is the genuinely hard case and plan.org still specifies it as a rejection — storage already +allocated has the old shape and a new body reads its fields at the wrong offsets. Managed classes are the planned way +through, with an explicit migration at a frame boundary. Do not conflate the two: one is unbuilt, the other is decided. + + **From porting ten raylib examples** — the first code the language was pushed by that it was not designed around. Ranked by how often they were hit, top two first because they are walls rather than conveniences: diff --git a/emacs/MANUAL.md b/emacs/MANUAL.md index 95c5b98..43a6cf3 100644 --- a/emacs/MANUAL.md +++ b/emacs/MANUAL.md @@ -194,19 +194,39 @@ press rather than a timer. ## When a change is refused -Some changes cannot be made to a running program. Change a struct's layout, or a -function's signature, and the daemon refuses and tells you to restart. It is -being honest: the session's idea of your types has to describe the memory of the -process it is talking to, and after a layout change it no longer does. +Two different things wear the same refusal today, and only one of them is the +design. -**`C-c C-x`** (`flan-dev-restart-program`) is the way out. It stops the program, -rebuilds from source, starts it again, and reconnects. +### A changed signature — a placeholder, not a rule -The program's state goes with it. That is the whole cost, and it is why this is a -separate key rather than something `C-c C-c` quietly falls back to — losing your -game's state should be something you asked for. +The intended behaviour, and what plan.org specifies, is that changing a +function's signature makes a **new version** of it: new callers resolve the new +one, existing callers and any stored `Fn` value stay safely on the old one, and +the session **warns** at each tracked stale caller site so you know what to +re-evaluate. Nothing should have to restart. ---- +That needs function versions, trampolines and caller tracking, none of which are +built yet. Until they are, the session refuses rather than letting an +indirection cell hand old arguments to a new body — a wrong answer would be +worse than a refusal. `lib/session.ml` says so at the refusal itself, and +plan.org tracks it as open decision #6. + +So if you hit this: it is a limitation with a date on it, not how the language +is meant to work. + +### A changed struct layout — the genuinely hard one + +Rejected while live values of that struct exist, and this one plan.org does +still specify as a rejection. Storage already allocated has the old shape; a new +body would read its fields at the wrong offsets and nothing at run time would +say so. Managed classes are the planned way through — an explicit migration at a +frame boundary — and they are not built either. + +### The way out, for now + +`C-c C-x` stops the program, rebuilds from source, starts it again and +reconnects. It costs the program's state, which is why it is a key you press +rather than something `C-c C-c` quietly falls back to. ## Under the debugger diff --git a/web/index.html b/web/index.html index 87299f9..dbb2f25 100644 --- a/web/index.html +++ b/web/index.html @@ -116,6 +116,34 @@ th, td { text-align: left; padding: .38rem .6rem .38rem 0; border-bottom: 1px so th { font-weight: 600; color: var(--muted); } .scroll { overflow-x: auto; margin: 0 0 1.15rem; } +/* Tabs. The panels are visible by default and only become switchable once + the script adds .tabbed to
— with no script the page is the one long + document it has always been, which is also what a printer and a text browser + get. */ +.tabs { + display: flex; flex-wrap: wrap; gap: .4rem; + margin: 0 0 2rem; padding: 0 0 .6rem; + border-bottom: 1px solid var(--rule); +} +.tabs button { + font: inherit; font-size: .93rem; cursor: pointer; + background: none; border: 1px solid transparent; border-radius: 4px; + padding: .3rem .7rem; color: var(--muted); +} +.tabs button:hover { color: var(--link); } +.tabs button[aria-selected="true"] { + color: var(--text); background: var(--panel); border-color: var(--rule); +} +.tabs button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } +main:not(.tabbed) .tabs { display: none; } +main.tabbed .panel[hidden] { display: none; } + +.toc-group { + margin: 1.1rem 0 .3rem; font-size: .72rem; letter-spacing: .09em; + text-transform: uppercase; color: var(--muted); opacity: .75; +} +.toc-group:first-child { margin-top: 0; } + .lede { font-size: 1.1rem; } .note { color: var(--muted); font-size: .94rem; } @@ -190,6 +218,7 @@ footer { margin-top: 3.5rem; padding-top: 1.5rem; border-top: 1px solid var(--ru