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
83 lines
3.0 KiB
Plaintext
83 lines
3.0 KiB
Plaintext
; Fieldtest series-step1.3 — Float element + warmup-aware emission using
|
|
; the len/total_count bookkeeping distinction.
|
|
;
|
|
; Task: "compute a rolling sum over a window of 4 Float samples, but only
|
|
; emit a value once the window is actually full (warmup suppressed)." A
|
|
; downstream author reaches for the bookkeeping fields to gate emission:
|
|
; emit iff the live count has reached the lookback. This is the warmup
|
|
; pattern, but gated on `Series.len` (the saturating live count) rather
|
|
; than `Series.total_count` (the monotone total) — both are offered and
|
|
; the author must pick the right one.
|
|
;
|
|
; Using `len` as the gate means: emit the first time len hits 4, and on
|
|
; every push after that (len stays saturated at 4). Using total_count
|
|
; would behave identically here BUT the author who wants "full window"
|
|
; semantics should reach for len; this example pins that reading.
|
|
;
|
|
; Stream: 2.0 4.0 6.0 8.0 10.0 12.0 (lookback 4)
|
|
; push 2.0 len 1 (warmup, suppressed)
|
|
; push 4.0 len 2 (warmup, suppressed)
|
|
; push 6.0 len 3 (warmup, suppressed)
|
|
; push 8.0 len 4 -> sum {2,4,6,8} = 20.0
|
|
; push 10.0 len 4 -> sum {4,6,8,10} = 28.0 (2.0 evicted)
|
|
; push 12.0 len 4 -> sum {6,8,10,12} = 36.0 (4.0 evicted)
|
|
; Expected stdout (one per line): 20.0 28.0 36.0
|
|
|
|
(module series-step1_3_warmup_rolling_sum
|
|
|
|
(data FloatList
|
|
(ctor FNil)
|
|
(ctor FCons (con Float) (con FloatList)))
|
|
|
|
(fn sum_window_step
|
|
(type (fn-type
|
|
(params (borrow (con Series (con Float))) (own (con Int)) (own (con Int)) (own (con Float)))
|
|
(ret (own (con Float)))))
|
|
(params s len i acc)
|
|
(body
|
|
(if (app eq i len)
|
|
acc
|
|
(tail-app sum_window_step s len
|
|
(app + i 1)
|
|
(app + acc (app Series.at s i))))))
|
|
|
|
; Emit the rolling sum only once the window is full (len == lookback=4).
|
|
(fn emit_if_full
|
|
(type (fn-type
|
|
(params (borrow (con Series (con Float))))
|
|
(ret (own (con Unit))) (effects IO)))
|
|
(params s)
|
|
(body
|
|
(if (app eq (app Series.len s) 4)
|
|
(seq (app print (app sum_window_step s (app Series.len s) 0 0.0))
|
|
(do io/print_str "\n"))
|
|
(do io/print_str ""))))
|
|
|
|
(fn run_stream
|
|
(type (fn-type
|
|
(params (own (con Series (con Float))) (own (con FloatList)))
|
|
(ret (own (con Unit))) (effects IO)))
|
|
(params s input)
|
|
(body
|
|
(match input
|
|
(case (pat-ctor FNil) (do io/print_str ""))
|
|
(case (pat-ctor FCons v rest)
|
|
(let s_new (app Series.push s v)
|
|
(seq (app emit_if_full s_new)
|
|
(tail-app run_stream s_new rest)))))))
|
|
|
|
(fn main
|
|
(type (fn-type (params) (ret (own (con Unit))) (effects IO)))
|
|
(params)
|
|
(body
|
|
(let s (new Series (con Float) 4)
|
|
(app run_stream s
|
|
(term-ctor FloatList FCons 2.0
|
|
(term-ctor FloatList FCons 4.0
|
|
(term-ctor FloatList FCons 6.0
|
|
(term-ctor FloatList FCons 8.0
|
|
(term-ctor FloatList FCons 10.0
|
|
(term-ctor FloatList FCons 12.0
|
|
(term-ctor FloatList FNil)))))))))))
|
|
)
|