A watch window, an address-rooted inspector, and structural typing that is just layout

This commit is contained in:
Joseph Ferano 2026-09-12 16:53:43 +07:00
parent 7ff6c27964
commit 135a780f3d

48
NEXT.md
View File

@ -653,6 +653,54 @@ exists for the expander, which cannot work that way.
`exported` and the refusal machinery already exist and take a second rule in one line, but there is no way for a
package to *mark* a name private, and adding one means a parser change.
## Decided in discussion — three more, all approved and none started
**A watch window, ported from the author's Clojure one.** `~/Development/siam-farmer/watch.el` is the working
original; read it first. Its design, and the parts to keep:
- **The program defines what is shown.** Emacs polls one function — `(watch/render)` — and paints the string it
returns. There is no watch-expression machinery, no per-variable registration, no UI for building a query. The user
writes a function in the game.
- **Async, not synchronous.** A sync request on a 0.2s timer blocks Emacs's UI thread every tick. The original says so
in a comment, having evidently learned it.
- **`replace-buffer-contents`, not erase-and-insert.** It diffs, so point and scroll survive every tick; erasing yanks
the cursor to the top five times a second.
- Nothing is appended — the buffer is always the current snapshot.
**The one thing that does not port, and it decides the design.** In Clojure an eval is cheap. Here `eval-expr`
*compiles a module and `dlopen`s it* — tens of milliseconds and a new `.so` each time, in a directory nothing sweeps.
Polling at 5Hz would produce hundreds of shared objects a minute. So **the watch thunk must be compiled once and then
called repeatedly**, which makes this a daemon feature rather than something the Emacs side can do alone: a `watch`
op that compiles on first use and a cheap re-invoke per tick.
The author also raised **ghost text** as an alternative or addition to a dedicated buffer — values shown inline at the
code they belong to. Not designed; the buffer is the port, ghost text is a further question.
**The inspector gets a second way to start: an address and a type.** Closes the hole in `DISCUSS.md` item 1, where `i`
on a local in any frame but the innermost evaluates a name wherever the evaluator stands rather than in that frame, and
may silently inspect something else.
The inspector navigates by rewriting *expressions*`(.pos b)` where the last was `b` — and `l` pops back. That is
why the obvious fix, rooting it at the slot's address, was rejected: an address is not an expression, so the first
`RET` has nothing to build from. **The shadow stack changed this.** The daemon now has a frame's address and every
slot's type, so the second rooting mode is cheap: start from an address plus a type, and stepping into a field is
address-plus-offset with the field's type. `Render.render` already does exactly that arithmetic for locals, and
navigation keeps working, which was the objection.
**Structural typing requires identical layout — same fields, same types, same order.** Settled by the author, and it
makes the feature simple rather than hard: structural compatibility becomes "the same memory", which costs nothing at
run time and needs no copy, no reordering and no adaptor. The motivating case is `{.x 1.0 .y 1.0}` and that order is
natural anyway.
**Flexible field order waits for classes, deliberately.** A class has an implementation-defined representation, so the
compiler owns the layout and field order stops being observable — any order can match. That is the right place to pay
for flexibility, because a class already carries identity and metadata, and a `Vector2` should pay for neither. See the
`defclass` entry: `Handle` is the gate.
Note what this settles from the earlier discussion: writability was the question that decided layout, and requiring
identical layout answers it — fields are writable on the ordinary terms, by value a copy and through a `(Ptr T)` the
original, with no special case.
## Blocked and unfinished
Everything below was found, decided or half-built and then stopped. Each says what blocks it. Nothing here is a