127 lines
7.0 KiB
Org Mode
127 lines
7.0 KiB
Org Mode
* Stuff I've found
|
||
** Why do I need to call flan-dev to open another window?
|
||
let: flan: the program exited; restart flan dev
|
||
** I can't eval a toplevel defvar, need to eval-defun (C-c C-c)
|
||
For Flan’s intended live-program workflow, C-x C-e on any complete top-level form should do the natural thing:
|
||
|
||
- expression → compile/run temporary thunk; print its value
|
||
- defn, defvar, defmacro, etc. → compile/install it; report what changed
|
||
|
||
The compiler already has both paths. The current split is an editor/UI artifact: C-x C-e is wired directly to eval-expr, while C-c C-c is wired to declaration reload. It is not a fundamental limitation.
|
||
|
||
A good fix would make C-x C-e context-aware: if the enclosing form is top-level, send it through the declaration evaluator; otherwise use expression evaluation. Then C-c C-c can remain a convenient explicit “reload this definition” alias, but not the only way defvar works.
|
||
** I can't eval a top level Vec
|
||
slurp returns (Vec u8), an owning, move-only buffer. Flan currently forbids every move-only global because it has no global ownership/lifetime model: any function could read and free it, while ownership tracking only exists within one function.
|
||
|
||
For data that is fixed at build time, use an embedded immutable array instead:
|
||
|
||
(defconst the-data (embed "game-data.edn"))
|
||
|
||
That produces a fixed [u8], not a heap-owning Vec, so it can live globally. It also resolves relative to sand.flan.
|
||
|
||
If game-data.edn genuinely must be loaded at runtime, then today it has to be owned by a local—typically load it in main and pass it through the functions that need it. For a game-wide runtime-owned data asset, that is a missing language/runtime feature, not a bad use case on your part.
|
||
** The edn module seems to need a struct declaration, it should do both; go into a struct but also return a Map with Vecs and Sets when we don't provide a type
|
||
** defenum needs optional autoincrementing discriminants
|
||
** We need a javascript backend so we can reach the world
|
||
** We need to have C-style unions, maybe those are called defunion, and then sum types are defdata or deftype
|
||
|
||
* Decisions, 2026-09-17
|
||
|
||
** 1. Re-runnable main — DISPATCHED
|
||
The process does not actually die. [flan_exit_hook] is [flan_merged_exit]
|
||
(lib/dev.ml:2669): it flushes, reclaims fd 1, and parks in [for (;;) pause()].
|
||
What is missing is a way to wake it. The park becomes a condvar wait, a daemon
|
||
op signals it, and the main thread — not a new one, because raylib wants the
|
||
main thread — re-enters [flan_program_main]. [alive] gains a third state,
|
||
parked-and-re-runnable, and each of the ten guard sites decides for itself
|
||
whether it accepts one.
|
||
|
||
Globals are NOT reset between runs. That is the CL/Clojure semantics asked for:
|
||
the process never died, so a second (main) sees what the first one left.
|
||
|
||
** 2. C-x C-e on a top-level form — QUEUED behind 1
|
||
Same file as 1 (emacs/flan-dev.el), so it waits rather than merging by hand.
|
||
No design questions; the note specifies it.
|
||
|
||
** 3. Runtime-loaded owning globals — DISPATCHED
|
||
Not a missing global ownership model. One rule: a move-only global is legal,
|
||
and reading one is always a borrow, never a move. Nothing takes ownership,
|
||
nothing frees it, its lifetime is the process's. Sound precisely because the
|
||
lifetime question has a constant answer.
|
||
|
||
Mutable in place as well — a global Vec can be pushed to. Aliasing follows
|
||
whatever locals already do; no new borrow regime for globals that locals lack.
|
||
|
||
[embed] (lib/check.ml:4265) still covers build-time data and is untouched.
|
||
|
||
** 4. edn both typed and dynamic — QUEUED behind 3
|
||
Two projects, not one. (read-edn T bytes) does not exist — vendor/edn/edn.flan
|
||
is only a tokenizer, and the compile-time struct walk is NEXT.md item 9.
|
||
|
||
The dynamic half is blocked: a dynamic EDN value is recursive, so it is a Vec
|
||
whose element is move-only, which lib/check.ml:599 refuses and whose refusal
|
||
names [drop] as the fix. So [drop] gets unstashed — it now has the customer
|
||
NEXT.md:225 said it lacked. Full spec-memory.md:338 scope: recursive teardown
|
||
AND the hook. Queued behind 3 because both land in check.ml.
|
||
|
||
** 5. defenum autoincrement — DISPATCHED
|
||
C's rule: no value means previous+1, the first is 0, explicit and implicit mix.
|
||
|
||
Duplicates: an explicitly written one is an intended alias and is allowed. One
|
||
produced by autoincrement walking into a value another member holds is an
|
||
accident and is refused, naming both members.
|
||
|
||
** 6. JavaScript backend — HELD
|
||
wasm32 already works: test/wasm-run.mjs is a WASI host, the test table runs
|
||
wasm32 builds, web/index.html is in the tree. A second backend beside emit.ml
|
||
and x86.ml is the largest item here and the dev loop comes first.
|
||
|
||
** 7. defdata and defunion — QUEUED last
|
||
Today's [defunion] is already the tagged sum type. It is renamed [defdata],
|
||
and [defunion] becomes the C-style untagged one. Serves both FFI and type
|
||
punning, and cimport verifies it against the header where one exists —
|
||
cimport.ml:295 currently skips any record holding an anonymous union, leaving
|
||
the defstruct beside it unchecked.
|
||
|
||
Last, because the rename sweeps parse/check/emit/prelude/docs and every .flan
|
||
file, and would conflict with everything above.
|
||
|
||
* Open, found while working the list
|
||
|
||
** A transient signal -11 on the globals daemon
|
||
Seen once, in one of three consecutive test runs, by the agent doing the
|
||
C-x C-e work; the runs either side of it were clean. Not reproduced since —
|
||
three forced full runs (dune test --force) are green, 232 checks, 0 failures.
|
||
|
||
Worth remembering rather than chasing, because the daemon it appeared on is
|
||
one the move-only-global work (c124df3) changed: test_reload's fixture gained
|
||
a host global Vec and a run-time-new one. A teardown or a reload module that
|
||
defines rather than declares a global Vec would strand the block the live
|
||
process is using, which is exactly the shape a rare SIGSEGV takes. If it comes
|
||
back, start there.
|
||
|
||
* Landed on dev-loop
|
||
|
||
Items 1, 2, 3 and 5 are merged and green (dune test --force, 232 elisp checks,
|
||
0 failures). Items 4 (drop) and 7 (defdata) are still being written. Item 6 is
|
||
held.
|
||
|
||
** Re-run is merged, and does not work under --x86
|
||
Park and re-run live in the merged entry point's main(), and --x86 refuses the
|
||
merged daemon by design: a merged host exports every flan.* body for -rdynamic
|
||
and so interposes the prelude bodies of the LLVM-built macro module the
|
||
compiler loads into itself. --x86 therefore runs --two-process, where the
|
||
program is a child, and a child that finishes is genuinely Gone — there is
|
||
nothing to wake. [Program.rerun] answers with the two-process refusal rather
|
||
than the merged one, and a test pins it.
|
||
|
||
Re-run on x86 needs the merged daemon to accept --x86 first. Separate work.
|
||
|
||
** The headline complaint is verified fixed on sand.flan
|
||
Window opened, closed, the daemon reported parked, (:op "rerun") returned ok,
|
||
and xdotool found a live window from the second run.
|
||
|
||
The earlier claim that [flan dev sand.flan] failed with "unknown function
|
||
begin-drawing" was true only on the stale base the work started from, and was
|
||
retracted after a re-test. Nothing to chase.
|