diff --git a/FIX.org b/FIX.org index d0f93ab..21105ed 100644 --- a/FIX.org +++ b/FIX.org @@ -437,6 +437,56 @@ rename. typed-flan branch freezes the static language pre-dyn. All of it dispatches after the x86-dyn lane lands. The struct dyn-field refusal (01e60fa) is the stopgap 2 lifts. +** The two models, named 2026-09-19 +With a collector in the runtime, the direction has a shorter statement than +it used to. The dynamic paths mimic Clojure. The static paths mimic Odin. +Both carry a little more ML than either of them does. + +That is a tiebreaker, not a slogan. A question on the dyn side that Clojure +has already answered takes Clojure's answer unless there is a reason to +depart, and the same holds for Odin on the static side. Keywords, maps and +nil landed under that reasoning without it being written down yet. + +Common Lisp is consulted alongside Clojure on the dynamic side, and on some +questions it is the better authority of the two. The condition system is the +standing proof: handler-bind, the restarts and invoke-restart are Common +Lisp, and Clojure has nothing resembling them. handler-case is the same +lineage — Clojure's try/catch is the shape most reached for, but the form +being added is Common Lisp's, and it is named for the Lisp rather than the +Clojure because it is the unwinding half of a pair whose other half is +already CL's. + +Where the two disagree, the question is which one the rest of Flan already +agrees with. Conditions say Common Lisp. Maps, keywords and nil say Clojure. +Neither answer generalises to the other's territory. + +The ML share is the part neither model supplies — the type system, the +options, the exhaustive matching, and whatever a second ML surface would +eventually add if the deferred syntax question ever reopens. + +** handler-case, decided 2026-09-19 +Flan has handler-bind, which is the resuming handler: it runs where the +condition was signalled, with the stack still standing, and carries on by +invoking a restart. What it has no spelling for is the other half — unwind, +and answer the whole form with a value. Clojure spells that try/catch and +reaches for it constantly; the closest thing here is a handler-bind plus a +use-value dance that is far heavier than the intent, or a pre-check that +races the read it guards. + +The gap showed itself when edn/read-file stopped returning an Option. The +caller that used to write or-else against a None had nothing left to write, +because the missing file now arrives as a FileError condition and the only +concise way to answer a condition with a default did not exist. The shape +wanted is: + + (handler-case (edn/read-file "game-data.edn") + [(FileError [c] nil)]) + +which keeps read-file's decision intact — the caller still says what a +missing file means — while costing one form instead of a machine. Until it +lands, sand.flan guards the read with file-exists?, which is a stopgap and +racy, and should be rewritten the moment this exists. + ** 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