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