flan/test/programs/vec-global.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

59 lines
2.3 KiB
Plaintext

;;;; A global of move-only type, which is allowed, and the one rule that makes
;;;; it allowed: reading a global Vec is always a borrow and never a move. The
;;;; lifetime question ownership tracking exists to answer has a constant
;;;; answer here — the process's — so nothing may take the global and nothing
;;;; may free it, and with no owner to hand over there is no double free to
;;;; catch. The refusals that enforce that are in test_flan.ml.
;;;;
;;;; A zeroed Vec is a real empty Vec, so the global starts as one and is
;;;; loaded by whoever loads it. What this program pins is that the loading
;;;; happens once and survives: [entry] is called twice, the way a re-entered
;;;; main would be, and the second call finds the data the first one left.
(defonce the-data (Vec u8))
(defonce counts (Map u8 i64))
;; Reading it here is a borrow. So is reading it in [total] below, which is the
;; case the old rule could not express: two functions holding the same global
;; at once is fine exactly because neither of them can free it.
(defn loaded? [] bool (> (len the-data) 0))
(defn load [] ()
(when (not (loaded?))
(set the-data (vec-new u8))
(dotimes [i 5] (push the-data (u8 (* i 3))))))
(defn total [] i64
(let [s (i64 0)]
(dotimes [i (len the-data)] (set s (+ s (i64 (at the-data i)))))
s))
;; Mutating in place, through the global rather than through a copy of it. The
;; aliasing contract is the one every Vec has (spec-memory.md, "Borrowing"): a
;; push may reallocate and invalidate a slice taken before it, and that is the
;; programmer's, here as much as for a local.
(defn bump [] ()
(set (at the-data 0) (+ (at the-data 0) 1))
(push the-data 100))
(defn entry [] ()
(load)
(bump)
(put counts 1 (total))
(println (len the-data))
(println (total))
(match (get counts 1) (Some v) (println v) None (println "?")))
(defn main [] i32
(entry)
;; The second run. Nothing re-initialises the global between them, so the
;; length keeps climbing and the loaded data is the same block it was.
(entry)
(println (len (as-slice the-data)))
;; A copy is the one thing something else may own, and freeing that copy
;; leaves the global untouched.
(let [c (clone the-data)]
(println (len c))
(free c))
(println (len the-data))
0)