diff --git a/FIX.org b/FIX.org index 1aeb28b..d0f93ab 100644 --- a/FIX.org +++ b/FIX.org @@ -410,6 +410,17 @@ rename. typed-flan branch freezes the static language pre-dyn. 2. Per-type descriptors: dyn fields in structs/conditions become markable. 3. Typed containers into dyn as VIEWS — one descriptor word in the box, reads box the element, writes tag-check. Rides on 2. No copies. + DECIDED 2026-09-19: the descriptor is its own thing, not the slice type + reused. Two reasons. A dyn value is a single word and a slice is two, so + reusing the slice buys no allocation back — the descriptor goes on the + heap either way. And a slice carries where and how many but not of what, + which is the one fact dyn needs, since boxing a read and tag-checking a + write both require the element type. The descriptor is therefore pointer, + length, and element type: a slice plus the piece a slice is missing. + Left open until the lane is built: whether the descriptor points at the + container or is a fattened slice stored beside it. That only bites if the + container can grow and move, which would leave a push through dyn holding + a stale pointer. 4. nil: arrives with maps. nil <-> None at (Option T) boundaries, trap at bare T, (Some nil) unconstructible. 5. Typed = and != grow strings: bytewise, length + same-pointer fast paths, @@ -419,7 +430,73 @@ rename. typed-flan branch freezes the static language pre-dyn. 7. dyn if: truthiness (nil/false are false, all else true). Typed stays strict bool. 8. Return slot stays mandatory (dyn or ()) — the parse ambiguity it closes - is real; revisit only if it grates. + is real; revisit only if it grates. SETTLED 2026-09-19, reconfirmed with + the author: both spellings stay legal, () is not collapsing into dyn. + No work follows from this one. All of it dispatches after the x86-dyn lane lands. The struct dyn-field refusal (01e60fa) is the stopgap 2 lifts. + +** The JS backend answers string equality wrongly, parked 2026-09-19 +Typed = and != grew strings in daed039, and the JS dialect was not taught the +case. A string there is a view object and the arm at lib/js.ml:856 compares +with ===, which asks whether two views are the same object rather than +whether their bytes agree. The arm was unreachable for strings until the +checker stopped refusing them, so the lane made an existing hole live without +touching the file. Equal literals still answer true, because equal literals +intern to one view, which is what makes the wrong answer quiet rather than +obvious: (= s (string (slice (bytes s) 0 3))) is true natively and false +under --target=js. + +The author parked it. JS stays deprioritised and the fix is not queued. The +option on the table when it is picked up again is a loud refusal in that arm +rather than a real implementation, so the dialect says it cannot do this +instead of saying something false. + +** Sweep policy, decided 2026-09-19 +A lane runs the fast check and nothing more. `dune test` is the whole of a +lane's obligation, and it is judged by reading the printed output rather than +by the exit status, which stays 0 even when the acceptance runner prints +failures. Running one program directly to capture its real output for an +acceptance row is still expected; that is cheap. What a lane may no longer do +is sweep. + +The x86 survey and the sanitizer sweep run once, after several lanes have +landed, and whatever they turn up is dispatched as fixes in a single batch. +The reason is arithmetic: a survey walks all 156 programs across three modes, +and a lane that touches a handful of them was paying that cost in full to +learn nothing about the rest. Paid once for several lanes, the same sweep +answers the same question at a fraction of the wall clock. The consequence to +accept is that a lane is reviewed on its code rather than on sweep numbers it +no longer produces, which is what the review before a merge is for. + +* Surface syntax discussion, 2026-09-19 +The author wants an F#-ish indentation-based ML surface living side by side +with s-expressions, not replacing them. The languages that disappear for the +author, in the order named: Python first, then Odin, then F#. That ordering +is the case for why Flan's own parens might be costing more than they look +like they cost. + +The architecture agreed if it is ever built: one AST, the existing forms +unchanged, and a second reader in front of it. Macros stay usable from +either surface, since they operate on the same AST either way. A Nim-style +quote-block was floated as the way a macro's own body could be written in +the ML syntax rather than in s-expressions, without needing a third +representation. + +Middle options came up and were set aside rather than chosen. Parinfer stays +an editor trick — it never changes the language, only how parens are typed, +so it does not touch the actual complaint. Wisp and sweet-expressions +(indentation implying the parens) were considered and are closer to a real +second surface than Parinfer, but still read as a compromise rather than the +ML syntax the author actually wants. A simplified in-paren syntax was also +on the table and rejected on the same grounds — it thins the parens without +removing them. Rhombus was named as the maximal reference point: whatever a +full second surface costs, Rhombus is roughly what it costs to do properly. + +Decided: deferred, no spike queued. The author's working hypothesis is that +the friction with Clojure may not be the parens at all — it may be +immutability, and the discipline of planning a shape ahead of time that +comes with it. The plan is to write imperative Flan as it stands and see +whether the parens still grate once that variable is gone. Revisit this once +that evidence exists.