flan/test/programs/defer-let.flan
Joseph Ferano a4c6b996ff def re-runs its initialiser, and defvar is renamed defonce
The trio the author decided on 2026-09-20 is now all built: def is CL's
defparameter — its initialiser runs on every daemon re-run, unguarded, so
an edited initialiser repaints the same storage on C-c C-c plus re-run —
defonce (Clojure's name for CL's defvar, per the author) initialises once
behind the .init~once. flag, and defconst stays the image.

One parse arm reads both forms; the difference is Ast.reinit, carried to
Tast.global's grerun. Emit.startup_plan gives a def no guard flag, and
Check.check_global lifts every def initialiser — zero and literal
included — into global/<n>, so the host's startup reaches it through the
function cell and a re-evaluated def swaps it (Session's def_inits;
Emit.redefinition declares the cell for a non-sibling target). The old
defvar spelling is refused with the rename and both compiling spellings,
and every program, test, doc and editor list is swept — except sand.flan,
the author's live WIP, whose seven defvar lines are flagged in FIX.org
and keep its three dependent tests red on this branch.
2026-09-21 07:12:04 +07:00

73 lines
2.5 KiB
Plaintext

;;;; defer in a let whose extent is the function body.
;;;;
;;;; A let is not a frame here: its bindings are function slots like any other,
;;;; and nothing is released at scope exit (spec-memory.md, "When storage is
;;;; released"). So a let at the top level of a function body has exactly the
;;;; function's extent, a defer written in it always registers, and it is as
;;;; safe as one written at the top level. A let nested inside such a let has
;;;; the same extent and the same permission.
;;;;
;;;; The numbers differ per failure, so a wrong answer names its own cause.
(defonce order i64)
(defn note [n i32] () (set order (+ (* order 10) (i64 n))))
;;; One defer in a let, with the acquisition it is paired with above it. This
;;; is the shape the relaxation exists for: acquire, defer the release beside
;;; it, then use it.
(defn one [] i64
(let [a 1]
(defer (note a))
(note 9))
order)
;;; Two resources in one let. This is the case a flag granted once per block
;;; instead of once per form gets wrong: the first defer registers and the
;;; second is refused.
(defn two [] ()
(let [a 1 b 2]
(defer (note a))
(defer (note b))
(note 9)))
;;; A nested let still has the function's extent, so a defer in it registers
;;; too — and it registers *later* than the outer one, so it runs first.
(defn nested [] ()
(let [a 1]
(defer (note a))
(let [b 2]
(defer (note b))
(note 9))))
;;; Registration order is one order across the boundary: a defer at the top
;;; level and a defer inside a let interleave by where they are written, not by
;;; which construct they are in. Written 1, 2, 3; run 3, 2, 1.
(defn mixed [] ()
(defer (note 1))
(let [x 2]
(defer (note x))
(defer (note 3))
(note 9)))
;;; An early return runs them too, and innermost-first, exactly as it does for
;;; a defer at the top level. The let's binding is still live when the defer
;;; reads it, because the slot is the function's.
(defn early [] i64
(let [a 1]
(defer (note a))
(let [b 2]
(defer (note b))
(return 7)))
0)
(defn main [] i32
(set order 0) (print (one)) (println "") ; 9 — read before the defer runs
(set order 0) (one) (print order) (println "") ; 91
(set order 0) (two) (print order) (println "") ; 921 — reverse of writing
(set order 0) (nested)(print order) (println "") ; 921
(set order 0) (mixed) (print order) (println "") ; 9321
(set order 0) (print (early)) (println "") ; 7
(print order) (println "") ; 21
0)