The author: "I think I prefer length over len, because then I'll use len as the variable name". One arm in check.ml, one row in the table beside it, and every (len x) in lib, test, examples, vendor, spike, docs, web, emacs, plan.org and NEXT.md rewritten. Shadowing and builtin/ had already taken most of the sting out: a (defn len ...) was legal and won in its own file, and builtin/len reached past it. What was left is that len was still a builtin — the defn earned a warning, and a wrapper had to say builtin/ at every inner call. Now there is nothing under the short name: len is an ordinary identifier in every position, which is what (let [len (length xs)] ...) wants. length takes over as shadowing's worked example rather than the feature losing one. shadow-builtin.flan, builtin-qualified.flan, pkgs/shadowed and the builtin/ rows in test_flan move to it and go on testing shadowing. A call to a len nothing defines is answered where an unknown function is, after every table and after the shadowing guard, so a program with its own len never reaches it. The sentence is said rather than guessed at — len and length are three edits apart and the did-you-mean's net is one — and the call is written back out through spell_arg, as-slice's spelling lifted out of it and now shared, so what is printed compiles. sand.flan:33 still calls the old name and is the author's to change; until it does, test_acceptance and test_session abort there. Both were run green against a copy with that one line changed. FIX.org says so.
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 (length xs)))
|
|
|
|
(defn wide [x i32] f32 (f32 x))
|
|
(defn 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 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 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 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 even?))]
|
|
(show (slice v)) ; 2 4
|
|
(free v))
|
|
(print builds) (println "") ; 1
|
|
0)
|