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 is-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 is-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 (length xs)))
|
|
|
|
(defn wide [x i32] f32 (f32 x))
|
|
(defn is-bigf [x f32] bool (> x 2.5))
|
|
|
|
(defn show [v [i32]] ()
|
|
(dotimes [i (length 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 (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 is-even))]
|
|
(show (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 is-even) (map double))]
|
|
(show (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 (slice v)) ; 6 2 4
|
|
(show (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 is-bigf))]
|
|
(dotimes [i (length 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 is-even))]
|
|
(show (slice v)) ; 2 4
|
|
(free v))
|
|
(print builds) (println "") ; 1
|
|
0)
|