;;;; 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]) (defvar 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)