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.
114 lines
4.7 KiB
Plaintext
114 lines
4.7 KiB
Plaintext
;;;; (array-fill [r c] v) and (array-gen [r c] f) — a fixed array as a value.
|
|
;;;;
|
|
;;;; DISCUSS.org's "need a value-producing array constructor": (array n T) is
|
|
;;;; the zeroed array and dotimes is Unit, so neither could be the initialiser
|
|
;;;; expression of a declaration. These are expressions, so they compose where
|
|
;;;; a bracket literal does — including as a defonce's initialiser, which is the
|
|
;;;; line the note was written about.
|
|
;;;;
|
|
;;;; The dimensions are in brackets and are the same compile-time lengths the
|
|
;;;; [n T] type spelling takes: an integer or a constant's name.
|
|
|
|
(defconst rows 3)
|
|
(defconst cols 4)
|
|
|
|
;; The line from the note. A typed declaration with a computed initialiser,
|
|
;; which is the startup-lifted path a defonce already had.
|
|
(defonce grid [rows [cols u8]] (array-fill [rows cols] 255))
|
|
|
|
;; One index per dimension, i32 each, and the return type is the element type.
|
|
(defn cell [r i32 c i32] i32 (+ (* r 100) c))
|
|
|
|
(defn one [i i32] i32 (* i i))
|
|
|
|
;; Row-major order is pinned, so a generator that counts observes it: this one
|
|
;; is called once per element and answers the call number, so the array it
|
|
;; fills is 0 1 2 ... in the order the elements are written.
|
|
(defonce ticks i32)
|
|
|
|
(defn tick [r i32 c i32] i32
|
|
(set ticks (+ ticks 1))
|
|
(- ticks 1))
|
|
|
|
;; An aggregate element: the store each loop pass writes is a struct copy.
|
|
(defstruct Cell [row i32 col i32])
|
|
|
|
;; Counts its own calls, for the evaluated-once line below.
|
|
(defonce calls i32)
|
|
|
|
(defn bump [] i32
|
|
(set calls (+ calls 1))
|
|
7)
|
|
|
|
(defn main [] i32
|
|
;; Rank 1.
|
|
(let [a (array-fill [5] 7)]
|
|
(print (at a 0)) (print " ") (print (at a 4)) (println "")) ; 7 7
|
|
|
|
;; Rank 2, and the element type is the fill value's.
|
|
(let [b (array-fill [2 3] (f32 1.5))]
|
|
(print (at b 1 2)) (println "")) ; 1.5
|
|
|
|
;; Rank 3.
|
|
(let [c (array-fill [2 2 2] -1)]
|
|
(print (at c 0 0 0)) (print " ") (print (at c 1 1 1)) (println "")) ; -1 -1
|
|
|
|
;; A dimension may be a constant's name, exactly as in [rows [cols u8]].
|
|
(let [d (array-fill [rows cols] 1)]
|
|
(print (at d 2 3)) (println "")) ; 1
|
|
|
|
;; The generator, rank 1: element i is i*i.
|
|
(let [g (array-gen [5] one)]
|
|
(print (at g 0)) (print " ") (print (at g 3)) (print " ")
|
|
(print (at g 4)) (println "")) ; 0 9 16
|
|
|
|
;; The generator, rank 2. Element [i][j] is i*100+j, which pins the index
|
|
;; arguments: the first is the outer index and the second the inner one, and
|
|
;; a form that passed them the other way round would print 1 and 300 here.
|
|
(let [h (array-gen [rows cols] cell)]
|
|
(print (at h 0 0)) (print " ") (print (at h 0 1)) (print " ")
|
|
(print (at h 1 0)) (print " ") (print (at h 2 3)) (println "")) ; 0 1 100 203
|
|
|
|
;; Row-major, pinned. [tick] answers the call number, so the element that
|
|
;; was written first holds 0 — and with four columns, [1][0] is the fifth.
|
|
(let [t (array-gen [rows cols] tick)]
|
|
(print (at t 0 0)) (print " ") (print (at t 0 1)) (print " ")
|
|
(print (at t 1 0)) (print " ") (print (at t 2 3)) (println "")) ; 0 1 4 11
|
|
|
|
;; The defonce from the top: 255 everywhere, read back as an i32 so the
|
|
;; printed value is the number and not a byte.
|
|
(print (i32 (at grid 0 0))) (print " ")
|
|
(print (i32 (at grid 2 3))) (println "") ; 255 255
|
|
|
|
;; An array value copies, which is what makes this a value and not a view:
|
|
;; writing through the copy leaves the global alone.
|
|
(let [copy grid]
|
|
(set (at copy 0 0) (u8 1))
|
|
(print (i32 (at copy 0 0))) (print " ")
|
|
(print (i32 (at grid 0 0))) (println "")) ; 1 255
|
|
|
|
;; The generator written in place — the canonical inline form. The brackets
|
|
;; are the only thing that says what [i] and [j] are: one i32 index per
|
|
;; dimension, and the element type is read off the body.
|
|
(let [q (array-gen [2 3] (fn [i j] (+ (* i 10) j)))]
|
|
(print (at q 0 0)) (print " ") (print (at q 1 2)) (println "")) ; 0 12
|
|
|
|
;; A struct-valued fill. The element is an aggregate, so what the loop
|
|
;; writes per element is a struct copy, on every backend.
|
|
(let [cs (array-fill [2 2] (Cell 3 4))]
|
|
(print (.row (at cs 0 0))) (print " ")
|
|
(print (.col (at cs 1 1))) (println "")) ; 3 4
|
|
|
|
;; Evaluated once: the fill *value* is bound before any loop runs, so a
|
|
;; call in that position is one call, however many elements get its answer.
|
|
(let [f (array-fill [4] (bump))]
|
|
(print (at f 3)) (print " ") (print calls) (println "")) ; 7 1
|
|
|
|
;; A zero dimension is an array with no elements, and the loop that fills it
|
|
;; runs no passes. Nothing to read, so the claim is that it compiles and the
|
|
;; program carries on.
|
|
(let [e (array-fill [0] 9)]
|
|
(println "empty ok"))
|
|
|
|
0)
|