flan/spike/js/p2-value-copies.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

88 lines
3.7 KiB
Plaintext

;;;; The evidence for lib/js.ml's value-semantics decision.
;;;;
;;;; A Flan struct and a fixed array are values: binding one copies it, and
;;;; emit.ml spends a memcpy at every such site. A JS object assigns by
;;;; reference, so an object mapping that does nothing aliases where the LLVM
;;;; build copied — and the divergence is invisible until something mutates a
;;;; copy, which is what every line below does.
;;;;
;;;; test/programs/values.flan already pins the two simplest cases. This goes
;;;; past them, to the ones a shallow copy would pass and a wrong one would
;;;; not: a struct inside a struct, a struct returned out of a function, an
;;;; element read out of an array of structs, and a global.
;;;;
;;;; Fable answers the same question the other way for F# structs on its JS
;;;; backend — it inserts no clone at all, and its Rust backend does — so this
;;;; is the property that says which of the two this dialect chose.
(defstruct Point [x i32 y i32])
(defstruct Box [lo Point hi Point n i32])
(defonce gp Point)
;; A parameter is a copy: writing to it must not reach the caller's value.
(defn bump [p Point] Point
(set (.x p) (+ (.x p) 100))
p)
;; A returned struct is a copy of whatever it names, not a second name for it.
(defn origin-of [b Box] Point
(.lo b))
(defn show [p Point] ()
(print (.x p)) (print " ") (println (.y p)))
(defn main [] i32
;; ── A let binding copies ────────────────────────────────────────
(let [a (Point {.x 1 .y 2})
b a]
(set (.x b) 99)
(show a) ; 1 2 — a must not have moved
(show b)) ; 99 2
;; ── A parameter copies ──────────────────────────────────────────
(let [a (Point {.x 1 .y 2})
c (bump a)]
(show a) ; 1 2
(show c)) ; 101 2
;; ── A nested struct copies with its container ───────────────────
(let [bx (Box {.lo (Point {.x 1 .y 1}) .hi (Point {.x 9 .y 9}) .n 3})
cy bx]
(set (.x (.lo cy)) 42)
(set (.n cy) 7)
(print (.x (.lo bx))) (print " ") (println (.n bx)) ; 1 3
(print (.x (.lo cy))) (print " ") (println (.n cy))) ; 42 7
;; ── A field read is a copy, not a view ──────────────────────────
(let [bx (Box {.lo (Point {.x 1 .y 1}) .hi (Point {.x 9 .y 9}) .n 3})
p (origin-of bx)]
(set (.x p) 55)
(print (.x (.lo bx))) (print " ") (println (.x p))) ; 1 55
;; ── A global copies both ways ───────────────────────────────────
(set gp (Point {.x 4 .y 5}))
(let [g gp]
(set (.x g) 0)
(show gp) ; 4 5
(show g)) ; 0 5
;; ── A fixed array is a value too ────────────────────────────────
(let [xs [1 2 3]
ys xs]
(set (at ys 0) 77)
(print (at xs 0)) (print " ") (println (at ys 0))) ; 1 77
;; ── An array of structs copies its elements ─────────────────────
;; An element read is a value too, so mutating what came out of one must
;; reach neither array. A shallow copy of the outer array would leave both
;; naming the same element object, and this would print 8 8 8.
(let [ps [(Point {.x 1 .y 1}) (Point {.x 2 .y 2})]
qs ps
e (at qs 0)]
(set (.x e) 8)
(print (.x (at ps 0))) (print " ")
(print (.x (at qs 0))) (print " ")
(println (.x e))) ; 1 1 8
0)