Two questions became decisions, so they move to what is left
This commit is contained in:
parent
244f83fe53
commit
f4f61ae8c9
60
DISCUSS.md
60
DISCUSS.md
@ -8,51 +8,7 @@ Settled decisions live in `NEXT.md`. Reasons for what already exists live in `BU
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Inspecting globals, separately, in the break buffer
|
## 1. What is `i` supposed to inspect in the stack section?
|
||||||
|
|
||||||
Locals are readable now; globals are not shown anywhere. They are arguably the more useful half in this language today,
|
|
||||||
because a Flan game keeps most of its state in top-level `defvar`s — `sand.flan` holds its entire grid that way.
|
|
||||||
|
|
||||||
What already exists: the daemon's `describe` op returns the program's globals by name, and `Session.render` walks a
|
|
||||||
concrete type to a printed value. The `eval-expr` path already renders a global by name inside the running program. So
|
|
||||||
the machinery is there and the question is mostly one of presentation — its own section in the break buffer, folded like
|
|
||||||
the stack, or a separate buffer.
|
|
||||||
|
|
||||||
The real question is **which** globals. A program has hundreds and a listing of all of them is the backtrace problem
|
|
||||||
again: the thing you want is buried by the thing you don't.
|
|
||||||
|
|
||||||
**Direction: one Globals section, scoped to the stack.** Per-frame was considered and dropped. The reason is that a
|
|
||||||
global is not part of a frame — it is program state the frame happened to touch — so nesting it under one implies an
|
|
||||||
ownership that is not there, and the same name then appears once per frame that reads it.
|
|
||||||
|
|
||||||
So: a section of its own, whose contents are the **union of the globals every frame on the current stack references**.
|
|
||||||
That keeps the compiler doing the choosing (the reference set per function is already known) without repeating anything,
|
|
||||||
and without falling back to listing all of a program's globals, which is the backtrace problem again.
|
|
||||||
|
|
||||||
Two refinements that came out of the same conversation:
|
|
||||||
|
|
||||||
- **Annotate each entry with which frames touch it.** This recovers what per-frame would have told you — "the whole
|
|
||||||
chain is reading this" reads differently from "only the innermost one is" — at no cost in duplication.
|
|
||||||
- **Order by the innermost frame that touches it.** A deep stack makes the union large again, and proximity to the
|
|
||||||
error is the ordering that puts the likely culprit on top.
|
|
||||||
|
|
||||||
What this deliberately does not solve: a global that nothing on the stack names but that you still want visible — the
|
|
||||||
sand grid while stopped in a helper that never mentions it. That is watching, and it is item 10 rather than part of
|
|
||||||
this.
|
|
||||||
|
|
||||||
## 2. The break buffer should appear by itself
|
|
||||||
|
|
||||||
Today a condition stops the program and the buffer only opens when `C-c C-b` is typed. It should `pop-to-buffer` the
|
|
||||||
moment the program stops.
|
|
||||||
|
|
||||||
What already exists: `flan-dev--absorb` inspects every reply for `:stopped`, and there is a poll for the case where no
|
|
||||||
reply is pending — the client already *knows* the moment it stops, and already updates the mode line from it. So this is
|
|
||||||
a hook at a point that exists, not new plumbing.
|
|
||||||
|
|
||||||
To decide: whether it steals focus or only displays; whether it should also fire for a `(pause)`, which is a deliberate
|
|
||||||
stop and arguably always wants the window; and what happens when it stops while point is mid-edit in another buffer.
|
|
||||||
|
|
||||||
## 3. What is `i` supposed to inspect in the stack section?
|
|
||||||
|
|
||||||
Raised as a question about intent, and it deserves one, because the current answer may be wrong rather than merely
|
Raised as a question about intent, and it deserves one, because the current answer may be wrong rather than merely
|
||||||
undocumented.
|
undocumented.
|
||||||
@ -67,7 +23,7 @@ frame-accurate and the inspector may not be. Options: root the inspector at the
|
|||||||
rooted; refuse `i` outside the innermost frame; or make the inspector frame-aware. Worth settling before anyone relies
|
rooted; refuse `i` outside the innermost frame; or make the inspector frame-aware. Worth settling before anyone relies
|
||||||
on it.
|
on it.
|
||||||
|
|
||||||
## 4. Annotating the IR and the disassembly with the source
|
## 2. Annotating the IR and the disassembly with the source
|
||||||
|
|
||||||
The compiler should interleave the originating Flan expression into both the emitted LLVM IR and the disassembly
|
The compiler should interleave the originating Flan expression into both the emitted LLVM IR and the disassembly
|
||||||
listing.
|
listing.
|
||||||
@ -80,7 +36,7 @@ already; what is missing is the interleaving.
|
|||||||
The IR half is nearly free. The disassembly half is more interesting and more valuable — it is what would let you see
|
The IR half is nearly free. The disassembly half is more interesting and more valuable — it is what would let you see
|
||||||
what one line of Flan actually costs, including the indirection cell a dev build puts on every cross-function call.
|
what one line of Flan actually costs, including the indirection cell a dev build puts on every cross-function call.
|
||||||
|
|
||||||
## 5. `def`, `defvar`, `defconst`
|
## 3. `def`, `defvar`, `defconst`
|
||||||
|
|
||||||
There is no `def`. There is `defvar` (mutable global) and `defconst` (compile-time constant, folded).
|
There is no `def`. There is `defvar` (mutable global) and `defconst` (compile-time constant, folded).
|
||||||
|
|
||||||
@ -93,7 +49,7 @@ accumulating.
|
|||||||
`defconst` under redefinition is the unclear one, as noted — it is folded into its use sites, so changing one is closer to
|
`defconst` under redefinition is the unclear one, as noted — it is folded into its use sites, so changing one is closer to
|
||||||
a recompile than to an assignment.
|
a recompile than to an assignment.
|
||||||
|
|
||||||
## 6. Structural typing, row polymorphism, anonymous structs
|
## 4. Structural typing, row polymorphism, anonymous structs
|
||||||
|
|
||||||
Worth noting before this is scoped: `types.ml` already does **structural equality** on resolved types, and a Flan struct
|
Worth noting before this is scoped: `types.ml` already does **structural equality** on resolved types, and a Flan struct
|
||||||
is exactly its C layout with no header or tag word. So the representation is already structural; what is nominal is the
|
is exactly its C layout with no header or tag word. So the representation is already structural; what is nominal is the
|
||||||
@ -104,7 +60,7 @@ spelling; how this interacts with the FFI, where the shim generates a C typedef
|
|||||||
with the planned managed classes, which are explicitly the *opposite* direction — identity and metadata rather than plain
|
with the planned managed classes, which are explicitly the *opposite* direction — identity and metadata rather than plain
|
||||||
layout.
|
layout.
|
||||||
|
|
||||||
## 7. A performant JS transpiler
|
## 5. A performant JS transpiler
|
||||||
|
|
||||||
Asked as a feasibility question. Bear in mind the web target already exists and ships real machine code via wasm, so this
|
Asked as a feasibility question. Bear in mind the web target already exists and ships real machine code via wasm, so this
|
||||||
is not the only route to a browser and the case for it needs stating: smaller artifacts, no wasm toolchain, debuggability
|
is not the only route to a browser and the case for it needs stating: smaller artifacts, no wasm toolchain, debuggability
|
||||||
@ -114,7 +70,7 @@ The hard parts are the ones the wasm target got for free from clang: the memory
|
|||||||
layout; JS is not), the FFI, and the fact that the whole raylib layer is C. A JS backend that cannot run raylib is a
|
layout; JS is not), the FFI, and the fact that the whole raylib layer is C. A JS backend that cannot run raylib is a
|
||||||
different product from the one that can.
|
different product from the one that can.
|
||||||
|
|
||||||
## 8. C interop as seamless as Zig's
|
## 6. C interop as seamless as Zig's
|
||||||
|
|
||||||
Today: `declare-c` names one C function per line, and the compiler generates the wrapper, the typedefs and the flattened
|
Today: `declare-c` names one C function per line, and the compiler generates the wrapper, the typedefs and the flattened
|
||||||
declaration. 175 of them for raylib. **No header is ever read, deliberately** — which means nothing can check that a
|
declaration. 175 of them for raylib. **No header is ever read, deliberately** — which means nothing can check that a
|
||||||
@ -128,7 +84,7 @@ trades an explicit, checkable list for an implicit surface. Worth weighing again
|
|||||||
is that a binding is one line and the wrapper is generated. The auto-kebab-case question is separable and much smaller —
|
is that a binding is one line and the wrapper is generated. The auto-kebab-case question is separable and much smaller —
|
||||||
and note the FFI currently keeps C's own spelling on purpose, so the mapping would need to be reversible.
|
and note the FFI currently keeps C's own spelling on purpose, so the mapping would need to be reversible.
|
||||||
|
|
||||||
## 9. Where `defclass` stands
|
## 7. Where `defclass` stands
|
||||||
|
|
||||||
Specified in `plan.org` and **deliberately not started** — its own last line says nothing happens until ordinary
|
Specified in `plan.org` and **deliberately not started** — its own last line says nothing happens until ordinary
|
||||||
`struct`, `Handle` and reload semantics work. Those have largely landed since that was written, so the gate may be closer
|
`struct`, `Handle` and reload semantics work. Those have largely landed since that was written, so the gate may be closer
|
||||||
@ -151,7 +107,7 @@ Also open and related: whether a *condition* can be a class, which decides wheth
|
|||||||
`NEXT.md` records the argument; decision 4 there took the cheaper parent-link route for now and explicitly left real
|
`NEXT.md` records the argument; decision 4 there took the cheaper parent-link route for now and explicitly left real
|
||||||
inheritance possible later.
|
inheritance possible later.
|
||||||
|
|
||||||
## 10. Watching variables
|
## 8. Watching variables
|
||||||
|
|
||||||
Raised while designing item 1, and deliberately separated from it.
|
Raised while designing item 1, and deliberately separated from it.
|
||||||
|
|
||||||
|
|||||||
27
NEXT.md
27
NEXT.md
@ -318,6 +318,33 @@ algorithmic cleverness. **jank is not the model** — it is Clojure, so its maps
|
|||||||
sharing, which plan.org rules out by name because shared structure destroys the clear ownership that is the whole
|
sharing, which plan.org rules out by name because shared structure destroys the clear ownership that is the whole
|
||||||
reason there is no collector.
|
reason there is no collector.
|
||||||
|
|
||||||
|
## Decided in discussion, queued
|
||||||
|
|
||||||
|
**Globals in the break buffer — one section, scoped to the stack.** Locals are readable; globals are not shown anywhere,
|
||||||
|
and in this language they are arguably the more useful half: a game keeps most of its state in top-level `defvar`s and
|
||||||
|
`sand.flan` holds its entire grid that way.
|
||||||
|
|
||||||
|
Not per frame. A global is not part of a frame — it is program state the frame happened to touch — so nesting it under
|
||||||
|
one implies an ownership that is not there and repeats the name once per frame that reads it. Instead: its own section,
|
||||||
|
whose contents are the **union of the globals every frame on the current stack references**, which keeps the compiler
|
||||||
|
doing the choosing (the per-function reference set is already known) without listing all of a program's globals.
|
||||||
|
|
||||||
|
Two refinements decided with it: **annotate each entry with which frames touch it**, which recovers what per-frame would
|
||||||
|
have told you at no cost in duplication; and **order by the innermost frame that touches it**, since a deep stack makes
|
||||||
|
the union large and proximity to the error is the ordering that puts the likely culprit on top.
|
||||||
|
|
||||||
|
The daemon already has the pieces — `describe` returns the globals, `Session.render` walks a concrete type to a printed
|
||||||
|
value, and the `layout` op established that a qualified name is an identity the daemon can resolve.
|
||||||
|
|
||||||
|
**The break buffer opens by itself when the program stops.** Today a condition stops the program and the buffer appears
|
||||||
|
only when `C-c C-b` is typed. `flan-dev--absorb` already inspects every reply for `:stopped` and a poll covers the case
|
||||||
|
where no reply is pending, so the client already knows the moment it happens and already moves the mode line from it —
|
||||||
|
this is a hook at a point that exists, not new plumbing.
|
||||||
|
|
||||||
|
Three things to settle while building it: whether it takes focus or only displays; whether `(pause)` should always take
|
||||||
|
the window, being a deliberate stop rather than a failure; and what it does when the program stops while point is
|
||||||
|
mid-edit in another buffer.
|
||||||
|
|
||||||
## Blocked and unfinished
|
## Blocked and unfinished
|
||||||
|
|
||||||
Everything below was found, decided or half-built and then stopped. Each says what blocks it. Nothing here is a
|
Everything below was found, decided or half-built and then stopped. Each says what blocks it. Nothing here is a
|
||||||
|
|||||||
@ -101,6 +101,7 @@
|
|||||||
;; over a mutable scan position, which is what a while loop is.
|
;; over a mutable scan position, which is what a while loop is.
|
||||||
(defn settle [row i32 col i32]
|
(defn settle [row i32 col i32]
|
||||||
(let [vel (+ gravity (at velocity row col))
|
(let [vel (+ gravity (at velocity row col))
|
||||||
|
some-point (rl/Vector2 {:x 15.0 :y 12})
|
||||||
y (min (- rows 1) (+ row (i32 vel)))]
|
y (min (- rows 1) (+ row (i32 vel)))]
|
||||||
(while (> y row)
|
(while (> y row)
|
||||||
(when (empty-at? y col)
|
(when (empty-at? y col)
|
||||||
@ -108,8 +109,8 @@
|
|||||||
(return))
|
(return))
|
||||||
(let [left? (and (> col 0) (empty-at? y (- col 1)))
|
(let [left? (and (> col 0) (empty-at? y (- col 1)))
|
||||||
right? (and (< col (- cols 1)) (empty-at? y (+ col 1)))]
|
right? (and (< col (- cols 1)) (empty-at? y (+ col 1)))]
|
||||||
(pause)
|
|
||||||
(when (or left? right?)
|
(when (or left? right?)
|
||||||
|
(pause)
|
||||||
(let [side (cond
|
(let [side (cond
|
||||||
(not left?) 1
|
(not left?) 1
|
||||||
(not right?) -1
|
(not right?) -1
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user