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.
95 lines
3.6 KiB
Plaintext
95 lines
3.6 KiB
Plaintext
;;;; into — a fused transformation, and not a transducer.
|
|
;;;;
|
|
;;;; What is asserted here is the thing a unit test cannot see: the loop that
|
|
;;;; comes out is one loop, and the values it produces are the ones the chain
|
|
;;;; describes in the order it was written.
|
|
;;;;
|
|
;;;; 1. The order of the transforms is the order of the stages. (filter even?)
|
|
;;;; before (map double) is not the same program as after it, and both are
|
|
;;;; here with different answers.
|
|
;;;; 2. There is no intermediate collection. `pulls` counts every call to the
|
|
;;;; transform functions: one pass over the source is one call per element
|
|
;;;; per stage it reaches, and a chain that built a Vec per stage would pull
|
|
;;;; a different number.
|
|
;;;; 3. A source that is already a name is only read. `src` is a (Vec i32),
|
|
;;;; which is move-only, and it is still alive and freeable afterwards.
|
|
;;;; 4. A source that is a call is evaluated once, not once per element.
|
|
|
|
(defonce pulls i32 0)
|
|
|
|
(defn double [x i32] i32
|
|
(set pulls (+ pulls 1))
|
|
(* x 2))
|
|
|
|
(defn even? [x i32] bool
|
|
(set pulls (+ pulls 1))
|
|
(= (% x 2) 0))
|
|
|
|
(defonce builds i32 0)
|
|
|
|
;; A source that is a call. It must be made once, however many elements come
|
|
;; out of it. It borrows rather than allocating, which is the shape a call in
|
|
;; this position wants: the macro binds the value to a name the caller cannot
|
|
;; see, so an owning temporary here would be a leak nobody can reach.
|
|
(defn source [xs [i32]] [i32]
|
|
(set builds (+ builds 1))
|
|
(slice xs 0 (len xs)))
|
|
|
|
(defn wide [x i32] f32 (f32 x))
|
|
(defn bigf? [x f32] bool (> x 2.5))
|
|
|
|
(defn show [v [i32]] ()
|
|
(dotimes [i (len v)] (print (at v i)) (print " "))
|
|
(println ""))
|
|
|
|
(defn main [] i32
|
|
;; No transforms: a copy into the destination named in the form.
|
|
(let [xs [7 8 9]
|
|
v (into xs (vec-new i32))]
|
|
(show (as-slice v)) ; 7 8 9
|
|
(free v))
|
|
|
|
;; map then filter.
|
|
(let [xs [1 2 3 4 5 6]
|
|
v (into xs (vec-new i32) (map double) (filter even?))]
|
|
(show (as-slice v)) ; 2 4 6 8 10 12
|
|
(free v))
|
|
|
|
;; filter then map, over the same source: a different answer, because the
|
|
;; stages are in the order they were written.
|
|
(let [xs [1 2 3 4 5 6]
|
|
v (into xs (vec-new i32) (filter even?) (map double))]
|
|
(show (as-slice v)) ; 4 8 12
|
|
(free v))
|
|
|
|
;; One pass and no intermediate collection. The two chains above pulled
|
|
;; 6 doubles + 6 evens, then 6 evens + 3 doubles: 21.
|
|
(print pulls) (println "") ; 21
|
|
|
|
;; A name as the source is read, not moved: src is still alive here.
|
|
(let [src (into [3 1 2] (vec-new i32))
|
|
v (into src (vec-new i32) (map double))]
|
|
(show (as-slice v)) ; 6 2 4
|
|
(show (as-slice src)) ; 3 1 2
|
|
(free v)
|
|
(free src))
|
|
|
|
;; A type-changing map: the chain's element name is rebound at the new type
|
|
;; by each stage, and the push sees the destination's element type. One name,
|
|
;; shadowed — a let binding's value is checked before its name is bound, so
|
|
;; each stage reads the stage before it.
|
|
(let [xs [1 2 3 4]
|
|
v (into xs (vec-new f32) (map wide) (filter bigf?))]
|
|
(dotimes [i (len v)] (print (at v i)) (print " "))
|
|
(println "") ; 3 4
|
|
(free v))
|
|
|
|
;; A source that is a call is bound once, so it is made once however many
|
|
;; elements come out of it.
|
|
(let [xs [1 2 3 4]
|
|
v (into (source (slice xs 0 4)) (vec-new i32) (filter even?))]
|
|
(show (as-slice v)) ; 2 4
|
|
(free v))
|
|
(print builds) (println "") ; 1
|
|
0)
|