(array-gen [3 4] (fn [i j] ...)) — the canonical form — was refused: check_fn saw no (Fn ...) want and no position to take types from. But the form knows them: one i32 index per dimension is the rank's own promise. check_array_gen now hands an inline fn its parameter types directly, with the annotated element type as the return want where the annotation reaches that deep, and the return left for the body to say where it does not — so a bare inline fn infers its element type the way a fill value does, and a body that disagrees with an annotated element is reported at the generator's answer, per element. Named defn generators check as before. check_fn grows a ?gen way in for exactly this: parameter types without a Fn want, return optional. An inferred-return body sees Unit as ctx.ret, a rough edge left rough on purpose. Pins: inline at rank 1 and 2, inferred element, annotated defvar, the per-element mismatch, inline arity. The acceptance program gains the inline form, a struct-valued fill (the per-element store is a struct copy), and evaluated-once (a counting fill value called one time for four elements) — riding the three existing rows, no new ones. And the FIX.org entry the pass never wrote: dims by the [n T] rule, one index per dimension, the Zero+While/Set/Pindex lowering with no backend edits, composition by nesting the forms, and this fix.
114 lines
4.7 KiB
Plaintext
114 lines
4.7 KiB
Plaintext
;;;; (array-fill [r c] v) and (array-gen [r c] f) — a fixed array as a value.
|
|
;;;;
|
|
;;;; DISCUSS.org's "need a value-producing array constructor": (array n T) is
|
|
;;;; the zeroed array and dotimes is Unit, so neither could be the initialiser
|
|
;;;; expression of a declaration. These are expressions, so they compose where
|
|
;;;; a bracket literal does — including as a defvar's initialiser, which is the
|
|
;;;; line the note was written about.
|
|
;;;;
|
|
;;;; The dimensions are in brackets and are the same compile-time lengths the
|
|
;;;; [n T] type spelling takes: an integer or a constant's name.
|
|
|
|
(defconst rows 3)
|
|
(defconst cols 4)
|
|
|
|
;; The line from the note. A typed declaration with a computed initialiser,
|
|
;; which is the startup-lifted path a defvar already had.
|
|
(defvar grid [rows [cols u8]] (array-fill [rows cols] 255))
|
|
|
|
;; One index per dimension, i32 each, and the return type is the element type.
|
|
(defn cell [r i32 c i32] i32 (+ (* r 100) c))
|
|
|
|
(defn one [i i32] i32 (* i i))
|
|
|
|
;; Row-major order is pinned, so a generator that counts observes it: this one
|
|
;; is called once per element and answers the call number, so the array it
|
|
;; fills is 0 1 2 ... in the order the elements are written.
|
|
(defvar ticks i32)
|
|
|
|
(defn tick [r i32 c i32] i32
|
|
(set ticks (+ ticks 1))
|
|
(- ticks 1))
|
|
|
|
;; An aggregate element: the store each loop pass writes is a struct copy.
|
|
(defstruct Cell [row i32 col i32])
|
|
|
|
;; Counts its own calls, for the evaluated-once line below.
|
|
(defvar calls i32)
|
|
|
|
(defn bump [] i32
|
|
(set calls (+ calls 1))
|
|
7)
|
|
|
|
(defn main [] i32
|
|
;; Rank 1.
|
|
(let [a (array-fill [5] 7)]
|
|
(print (at a 0)) (print " ") (print (at a 4)) (println "")) ; 7 7
|
|
|
|
;; Rank 2, and the element type is the fill value's.
|
|
(let [b (array-fill [2 3] (f32 1.5))]
|
|
(print (at b 1 2)) (println "")) ; 1.5
|
|
|
|
;; Rank 3.
|
|
(let [c (array-fill [2 2 2] -1)]
|
|
(print (at c 0 0 0)) (print " ") (print (at c 1 1 1)) (println "")) ; -1 -1
|
|
|
|
;; A dimension may be a constant's name, exactly as in [rows [cols u8]].
|
|
(let [d (array-fill [rows cols] 1)]
|
|
(print (at d 2 3)) (println "")) ; 1
|
|
|
|
;; The generator, rank 1: element i is i*i.
|
|
(let [g (array-gen [5] one)]
|
|
(print (at g 0)) (print " ") (print (at g 3)) (print " ")
|
|
(print (at g 4)) (println "")) ; 0 9 16
|
|
|
|
;; The generator, rank 2. Element [i][j] is i*100+j, which pins the index
|
|
;; arguments: the first is the outer index and the second the inner one, and
|
|
;; a form that passed them the other way round would print 1 and 300 here.
|
|
(let [h (array-gen [rows cols] cell)]
|
|
(print (at h 0 0)) (print " ") (print (at h 0 1)) (print " ")
|
|
(print (at h 1 0)) (print " ") (print (at h 2 3)) (println "")) ; 0 1 100 203
|
|
|
|
;; Row-major, pinned. [tick] answers the call number, so the element that
|
|
;; was written first holds 0 — and with four columns, [1][0] is the fifth.
|
|
(let [t (array-gen [rows cols] tick)]
|
|
(print (at t 0 0)) (print " ") (print (at t 0 1)) (print " ")
|
|
(print (at t 1 0)) (print " ") (print (at t 2 3)) (println "")) ; 0 1 4 11
|
|
|
|
;; The defvar from the top: 255 everywhere, read back as an i32 so the
|
|
;; printed value is the number and not a byte.
|
|
(print (i32 (at grid 0 0))) (print " ")
|
|
(print (i32 (at grid 2 3))) (println "") ; 255 255
|
|
|
|
;; An array value copies, which is what makes this a value and not a view:
|
|
;; writing through the copy leaves the global alone.
|
|
(let [copy grid]
|
|
(set (at copy 0 0) (u8 1))
|
|
(print (i32 (at copy 0 0))) (print " ")
|
|
(print (i32 (at grid 0 0))) (println "")) ; 1 255
|
|
|
|
;; The generator written in place — the canonical inline form. The brackets
|
|
;; are the only thing that says what [i] and [j] are: one i32 index per
|
|
;; dimension, and the element type is read off the body.
|
|
(let [q (array-gen [2 3] (fn [i j] (+ (* i 10) j)))]
|
|
(print (at q 0 0)) (print " ") (print (at q 1 2)) (println "")) ; 0 12
|
|
|
|
;; A struct-valued fill. The element is an aggregate, so what the loop
|
|
;; writes per element is a struct copy, on every backend.
|
|
(let [cs (array-fill [2 2] (Cell 3 4))]
|
|
(print (.row (at cs 0 0))) (print " ")
|
|
(print (.col (at cs 1 1))) (println "")) ; 3 4
|
|
|
|
;; Evaluated once: the fill *value* is bound before any loop runs, so a
|
|
;; call in that position is one call, however many elements get its answer.
|
|
(let [f (array-fill [4] (bump))]
|
|
(print (at f 3)) (print " ") (print calls) (println "")) ; 7 1
|
|
|
|
;; A zero dimension is an array with no elements, and the loop that fills it
|
|
;; runs no passes. Nothing to read, so the claim is that it compiles and the
|
|
;; program carries on.
|
|
(let [e (array-fill [0] 9)]
|
|
(println "empty ok"))
|
|
|
|
0)
|