flan/test/programs/algorithms.flan
Joseph Ferano 9ce51ba94e One slice over everything with elements, and the warning at the push
as-slice was a warning, not an operation. The input type already decides
which of the two things happens — a Vec can only be borrowed, an array or a
string can only be viewed, and no call site picks between them — so the second
name expressed no choice a reader could make. And it warned at the moment the
view is taken, which is the one moment nothing is wrong; the danger arrives
later, at the push. slice now takes a Vec at all three arities and as-slice
is gone.

(slice v lo) was free, and is the arity the Vec never had: the runtime already
reads a hi of -1 as "to the end", so the tail form passes the caller's lo and
the same -1 — no slot, no length read, no second evaluation. The merge is
entirely in the checker; the Vec path builds the flan_vec_as_slice call it
always built and neither backend has a line about any of it.

A Vec a call returned is refused at every arity, and not for the array's
reason. (slice (mk)) over an array dangles. (slice (make-vec)) does not — the
storage outlives the expression — but the header is a temporary, so nothing
can ever free the block. The refusal says that and names the let.

The name's own refusal sits in ordinary_call after every table, so a program
that defines an as-slice still reaches its own. It reads for somebody who has
never heard of the old name and writes the call back out, spelling each
argument that is a name or a number.

The warning moved to where it bites: BUILT.md gains a section beside the Vec
table and the push row points at it, spec-memory.md's Borrowing says the same.
Investigated and deliberately not built — a diagnostic for a live view at the
push. (reserve v 100) then a slice, a push and a read is correct code under
the contract the spec chose, so any flag on it is a false positive by the
language's own semantics rather than by an approximation. FIX.org has the
finding and the syntactic sketch that does not work.
2026-09-21 09:51:35 +07:00

121 lines
5.5 KiB
Plaintext

;;;; The slice family at its second and third element types.
;;;;
;;;; sort was the only sort in the language. These are the other two, and
;;;; they are copies rather than an abstraction: map, filter, reduce and a sort
;;;; taking a comparator all need a *function value*, which check.ml refuses
;;;; with "a function type is not implemented yet -- milestone 5". So the
;;;; honest form is the concrete one, and the claim this file makes is only
;;;; that the concrete ones are right.
(defn show-f32 [s [f32]] ()
(dotimes [i (len s)]
(print (at s i))
(print " "))
(println ""))
(defn show-fields [s [[u8]]] ()
(dotimes [i (len s)]
(print (string (at s i)))
(print " "))
(println ""))
(defn main [] i32
;; Every float literal is cast. A literal defaults to f64 and an array
;; literal has no context to say otherwise -- a let has no type annotation --
;; so [3.5 -1.0] is an [f64] and (sort) refuses it by type. The cast is
;; the only spelling available today.
;;
;; sort: duplicates, negatives, a zero and an odd length, which is the
;; input shape the i32 sort is tested on for the same reasons.
(let [xs [(f32 3.5) (f32 -1.0) (f32 0.0) (f32 3.5) (f32 -2.25) (f32 10.0) (f32 0.5)]]
(sort (slice xs 0 7))
(show-f32 (slice xs 0 7))) ; -2.25 -1 0 0.5 3.5 3.5 10
;; In place and ptr+len: sorting a subslice leaves its neighbours alone. That
;; is the whole content of the in-place claim, and a version that copied
;; would pass every test above and fail this one.
(let [xs [(f32 9.0) (f32 4.0) (f32 3.0) (f32 2.0) (f32 1.0) (f32 9.0)]]
(sort (slice xs 1 5))
(show-f32 (slice xs 0 6))) ; 9 1 2 3 4 9
;; Already sorted, reverse sorted, and a single element -- the three inputs
;; where an insertion loop with the comparison the wrong way round still
;; looks plausible.
(let [xs [(f32 1.0) (f32 2.0) (f32 3.0)]]
(sort (slice xs 0 3))
(show-f32 (slice xs 0 3))) ; 1 2 3
(let [xs [(f32 3.0) (f32 2.0) (f32 1.0)]]
(sort (slice xs 0 3))
(show-f32 (slice xs 0 3))) ; 1 2 3
(let [xs [(f32 7.0)]]
(sort (slice xs 0 1))
(show-f32 (slice xs 0 1))) ; 7
;; The empty slice must not read (at s -1).
(let [xs [(f32 7.0)]]
(sort (slice xs 0 0))
(show-f32 (slice xs 0 0))) ;
(let [xs [(f32 1.0) (f32 2.0) (f32 3.0) (f32 4.0)]]
(reverse (slice xs 0 4))
(show-f32 (slice xs 0 4))) ; 4 3 2 1
;; min, max and sum. The empty slice is None for the first two -- there is no
;; least f32 that is also an honest answer -- and the sum accumulates in f64,
;; which is why 16777216 + 1 does not absorb here the way it would in f32.
(let [xs [(f32 3.5) (f32 -1.0) (f32 10.0)]]
(match (min-of (slice xs 0 3)) (Some m) (print m) None (print "none"))
(print " ")
(match (max-of (slice xs 0 3)) (Some m) (print m) None (print "none"))
(print " ")
(print (sum-f32 (slice xs 0 3)))
(println "")) ; -1 10 12.5
(let [xs [(f32 1.0)]]
(match (min-of (slice xs 0 0)) (Some m) (print m) None (print "none"))
(print " ")
(print (sum-f32 (slice xs 0 0)))
(println "")) ; none 0
;; The accumulator's width, made visible. 2^24 is where an f32 stops having
;; a bit for 1, so an f32 running total absorbs both of these and the
;; difference below is 0. In f64 they both land, and it is 2.
(let [xs [(f32 16777216.0) (f32 1.0) (f32 1.0)]]
(print (- (sum-f32 (slice xs 0 3)) 16777216.0))
(println "")) ; 2
;; bytes<? is bytewise and unsigned, and explicitly not alphabetical: "Zebra"
;; comes before "apple" because 'Z' is 90. A prefix comes before what extends
;; it, which is the case a loop running only to (len a) reads off the end
;; for, and 0x80 above 0x00 is the case a signed byte gets backwards.
(print (bytes<? (bytes-view "a") (bytes-view "b"))) (print " ") ; true
(print (bytes<? (bytes-view "b") (bytes-view "a"))) (print " ") ; false
(print (bytes<? (bytes-view "a") (bytes-view "a"))) (print " ") ; false
(print (bytes<? (bytes-view "ab") (bytes-view "abc"))) (print " ") ; true
(print (bytes<? (bytes-view "abc") (bytes-view "ab"))) (print " ") ; false
(print (bytes<? (bytes-view "") (bytes-view "a"))) (print " ") ; true
(print (bytes<? (bytes-view "Zebra") (bytes-view "apple"))) ; true
(println "")
;; 0x00 below 0x80, which is the pair a comparison over a *signed* byte gets
;; backwards -- and there is no \x escape in the reader, so these are built
;; as u8 arrays rather than written as string literals.
(let [lo [(u8 0)]
hi [(u8 128)]]
(print (bytes<? (slice lo 0 1) (slice hi 0 1))) (print " ") ; true
(print (bytes<? (slice hi 0 1) (slice lo 0 1))) ; false
(println ""))
;; sort-bytes over the fields split out of one buffer. The slices move and
;; the bytes never do, so this sorts a borrowed view of a string literal --
;; which an in-place byte sort could not, since a literal lives in .rodata.
(let [f (split (bytes-view "pear,apple,Fig,apple,banana") \,)]
(sort-bytes (slice f))
(show-fields (slice f)) ; Fig apple apple banana pear
(free f))
;; And the round trip the whole second tier is for: split, sort, join.
(let [f (split (bytes-view "delta,alpha,charlie,bravo") \,)]
(sort-bytes (slice f))
(let [j (join (slice f) (bytes-view " < "))]
(println (string (slice j))) ; alpha < bravo < charlie < delta
(free j))
(free f))
0)