3b3c8c4f07
Post-ship field test of the shipped `series` library extension as a downstream Form-A author working from the public surface only. Five fixtures under examples/fieldtest/ across the construction/auto-import, ring-buffer/financial-indexing, len/total_count bookkeeping, param-in error-quality, and operator-naming axes; spec at docs/specs/0067-fieldtest-series-step1.md. Wins (carry-on): auto-import works with no `(import series)`; the financial index (0 = newest) maps directly onto the bounded-buffer mental model and stays correct after multiple wraps; the param-in diagnostic for a disallowed element type names the type, the type-variable, the qualified type, and the allowed set. Findings routed: - [bug] An owned ADT threaded through tail-recursion whose base case neither returns nor consumes it is never dropped — a general drop-soundness leak (a plain `Box` control with no Series/RawBuf also leaks). Series surfaces it heavily on the whitepaper's headline streaming pattern, and the committed reference examples/series_sma.ail itself leaks (live=8). Minimal repro: series-step1_5 (live=4). - [friction] `ail describe ... Series.push` (the canonical type-scoped form) reports module-not-found; only `series.push` / bare `push` resolve, and no command lists a type's op set. - [spec_gap] referencing a kernel base type (`RawBuf`) from a user ADT ctor field requires full qualification (`raw_buf.RawBuf`) while bare `Series` works in type-position via auto-import — the asymmetry is undocumented. The fixtures that leak still produce correct output; the leak is a separate observable under AILANG_RC_STATS and an orthogonal, pre-existing bug, not a Series defect. refs #61
76 lines
2.9 KiB
Plaintext
76 lines
2.9 KiB
Plaintext
; Fieldtest series-step1.2 — financial-indexing off-by-one at the wrap
|
|
; boundary + len-vs-total_count bookkeeping.
|
|
;
|
|
; Task: "keep the last 3 event codes seen on a stream; after the whole
|
|
; stream, dump the surviving window oldest-first, then report how many
|
|
; events were seen in total." This is the classic 'ring of last N' task.
|
|
;
|
|
; The interesting part is the DUMP: index 0 is the newest, so to print
|
|
; oldest-first I walk i from (len - 1) down to 0. After the buffer has
|
|
; wrapped more than once (total >> lookback), the financial index must
|
|
; still map correctly onto the underlying slot — this is the wrap-boundary
|
|
; off-by-one trap the axis calls out.
|
|
;
|
|
; total_count is the monotone "events ever seen" (does NOT saturate);
|
|
; len is the live window size (saturates at lookback=3). The task needs
|
|
; BOTH: len to bound the dump walk, total_count for the summary.
|
|
;
|
|
; Stream of 7 events: 10 20 30 40 50 60 70 (lookback 3)
|
|
; After all 7 pushes the window holds the newest 3: 50 60 70.
|
|
; Dumped oldest-first: 50, 60, 70.
|
|
; total_count = 7, len = 3.
|
|
; Expected stdout (one per line): 50 60 70 then "total=" path -> 7 and 3.
|
|
; Concretely the lines are: 50 60 70 7 3
|
|
|
|
(module series-step1_2_last_n_events
|
|
|
|
(data IntList
|
|
(ctor INil)
|
|
(ctor ICons (con Int) (con IntList)))
|
|
|
|
; Push every element of the list into the series, returning the series.
|
|
(fn fill
|
|
(type (fn-type
|
|
(params (own (con Series (con Int))) (own (con IntList)))
|
|
(ret (own (con Series (con Int))))))
|
|
(params s input)
|
|
(body
|
|
(match input
|
|
(case (pat-ctor INil) s)
|
|
(case (pat-ctor ICons v rest)
|
|
(tail-app fill (app Series.push s v) rest)))))
|
|
|
|
; Print the live window oldest-first: walk i from (len-1) down to 0.
|
|
; index 0 = newest, index (len-1) = oldest live element.
|
|
(fn dump_step
|
|
(type (fn-type
|
|
(params (borrow (con Series (con Int))) (own (con Int)))
|
|
(ret (own (con Unit))) (effects IO)))
|
|
(params s i)
|
|
(body
|
|
(if (app lt i 0)
|
|
(do io/print_str "")
|
|
(seq
|
|
(seq (app print (app Series.at s i)) (do io/print_str "\n"))
|
|
(tail-app dump_step s (app - i 1))))))
|
|
|
|
(fn main
|
|
(type (fn-type (params) (ret (own (con Unit))) (effects IO)))
|
|
(params)
|
|
(body
|
|
(let s0 (new Series (con Int) 3)
|
|
(let s (app fill s0
|
|
(term-ctor IntList ICons 10
|
|
(term-ctor IntList ICons 20
|
|
(term-ctor IntList ICons 30
|
|
(term-ctor IntList ICons 40
|
|
(term-ctor IntList ICons 50
|
|
(term-ctor IntList ICons 60
|
|
(term-ctor IntList ICons 70
|
|
(term-ctor IntList INil)))))))))
|
|
(seq
|
|
(app dump_step s (app - (app Series.len s) 1))
|
|
(seq
|
|
(seq (app print (app Series.total_count s)) (do io/print_str "\n"))
|
|
(seq (app print (app Series.len s)) (do io/print_str "\n")))))))))
|