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

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)