flow: bounded iteration combinators flow-while/flow-until + 6 tests
Some checks are pending
Test, Build, and Deploy / test-build-deploy (push) Waiting to run
Some checks are pending
Test, Build, and Deploy / test-build-deploy (push) Waiting to run
(flow-while pred body max) / (flow-until pred body max) re-run body threading the value while/until pred holds, capped at max steps for a deterministic bound (no unbounded loops in pure SX). 122/122 across 7 suites. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"total": 116,
|
||||
"passed": 116,
|
||||
"total": 122,
|
||||
"passed": 122,
|
||||
"failed": 0,
|
||||
"suites": {
|
||||
"basic": { "passed": 18, "total": 18 },
|
||||
@@ -9,7 +9,7 @@
|
||||
"recovery": { "passed": 8, "total": 8 },
|
||||
"distributed": { "passed": 19, "total": 19 },
|
||||
"api": { "passed": 12, "total": 12 },
|
||||
"combinators": { "passed": 11, "total": 11 }
|
||||
"combinators": { "passed": 17, "total": 17 }
|
||||
},
|
||||
"phases": { "phase1": "done", "phase2": "done", "phase3": "done", "phase4": "done", "phase5": "done" }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# flow-on-sx Scoreboard
|
||||
|
||||
**All tests pass: 116 / 116 across 7 suites. Phases 1-5 complete.**
|
||||
**All tests pass: 122 / 122 across 7 suites. Phases 1-5 complete.**
|
||||
|
||||
`bash lib/flow/conformance.sh`
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
| recovery | 8 | Phase 3: crash recovery — store export/import, resumable scan, restart-at-every-step, replay-log survival |
|
||||
| distributed | 19 | Phase 4: `remote-node` (7); `remote-failover` (6); replication + handoff across instances (6) |
|
||||
| api | 12 | Phase 5: introspection — `flow/status`, `flow/result`, `flow/list`, `flow/pending` |
|
||||
| combinators | 11 | Phase 5: `tap` side-effect, `recover` (fail-value), `map-flow` fan-over-list |
|
||||
| combinators | 17 | Phase 5: `tap`, `recover` (fail-value), `map-flow` fan-over-list, `flow-while`/`flow-until` bounded iteration |
|
||||
|
||||
## Architecture
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
;; defflow both binds the flow and registers it by name (flow-register!, in
|
||||
;; store.sx) so it can be re-resolved after a process restart.
|
||||
;; map-flow (Phase 5): run a node over each item of a list input, join results.
|
||||
;; flow-while / flow-until (Phase 5): bounded iteration — re-run body, threading
|
||||
;; the value, while/until pred holds, up to `max` steps (deterministic bound; no
|
||||
;; unbounded loops in pure SX).
|
||||
;;
|
||||
;; Phase 2 combinators (flow-control-src):
|
||||
;; branch / fail / failed? / fail-reason / try-catch / retry / timeout / tick
|
||||
@@ -35,7 +38,7 @@
|
||||
|
||||
(define
|
||||
flow-combinators-src
|
||||
"(define (flow-node f) f)\n (define (flow-id input) input)\n (define (flow-const v) (lambda (input) v))\n (define (flow-seq-step ns v)\n (if (null? ns) v (flow-seq-step (cdr ns) ((car ns) v))))\n (define sequence (lambda ns (lambda (input) (flow-seq-step ns input))))\n (define parallel (lambda ns (lambda (input) (map (lambda (n) (n input)) ns))))\n (define (map-flow node) (lambda (items) (map node items)))\n (define-syntax defflow\n (syntax-rules ()\n ((defflow nm body)\n (begin (define nm body) (flow-register! (quote nm) nm)))))")
|
||||
"(define (flow-node f) f)\n (define (flow-id input) input)\n (define (flow-const v) (lambda (input) v))\n (define (flow-seq-step ns v)\n (if (null? ns) v (flow-seq-step (cdr ns) ((car ns) v))))\n (define sequence (lambda ns (lambda (input) (flow-seq-step ns input))))\n (define parallel (lambda ns (lambda (input) (map (lambda (n) (n input)) ns))))\n (define (map-flow node) (lambda (items) (map node items)))\n (define (flow-while-step pred body input n)\n (if (<= n 0)\n input\n (if (pred input) (flow-while-step pred body (body input) (- n 1)) input)))\n (define (flow-while pred body max) (lambda (input) (flow-while-step pred body input max)))\n (define (flow-until-step pred body input n)\n (if (<= n 0)\n input\n (if (pred input) input (flow-until-step pred body (body input) (- n 1)))))\n (define (flow-until pred body max) (lambda (input) (flow-until-step pred body input max)))\n (define-syntax defflow\n (syntax-rules ()\n ((defflow nm body)\n (begin (define nm body) (flow-register! (quote nm) nm)))))")
|
||||
|
||||
(define
|
||||
flow-control-src
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
;; lib/flow/tests/combinators.sx — Phase 5: combinator library (tap, recover, map-flow).
|
||||
;; lib/flow/tests/combinators.sx — Phase 5: combinator library (tap, recover, map-flow, iteration).
|
||||
|
||||
(define flow-cmb-pass 0)
|
||||
(define flow-cmb-fail 0)
|
||||
@@ -74,4 +74,35 @@
|
||||
"(flow/start (sequence (map-flow (lambda (x) (* x 10))) (lambda (xs) (apply + xs))) (list 1 2 3))")
|
||||
60)
|
||||
|
||||
;; ── flow-while / flow-until (bounded iteration) ─────────────────
|
||||
(flow-cmb-test
|
||||
"flow-while: iterates while the predicate holds"
|
||||
(flow-m
|
||||
"(flow/start (flow-while (lambda (x) (< x 10)) (lambda (x) (+ x 1)) 100) 0)")
|
||||
10)
|
||||
(flow-cmb-test
|
||||
"flow-while: a false predicate leaves input unchanged"
|
||||
(flow-m
|
||||
"(flow/start (flow-while (lambda (x) (< x 0)) (lambda (x) (+ x 1)) 100) 5)")
|
||||
5)
|
||||
(flow-cmb-test
|
||||
"flow-while: respects the max-iteration bound"
|
||||
(flow-m "(flow/start (flow-while (lambda (x) #t) (lambda (x) (+ x 1)) 3) 0)")
|
||||
3)
|
||||
(flow-cmb-test
|
||||
"flow-while: doubles until past a threshold"
|
||||
(flow-m
|
||||
"(flow/start (flow-while (lambda (x) (< x 50)) (lambda (x) (* x 2)) 100) 3)")
|
||||
96)
|
||||
(flow-cmb-test
|
||||
"flow-until: iterates until the predicate becomes true"
|
||||
(flow-m
|
||||
"(flow/start (flow-until (lambda (x) (>= x 10)) (lambda (x) (+ x 3)) 100) 0)")
|
||||
12)
|
||||
(flow-cmb-test
|
||||
"flow-until: composes inside a sequence"
|
||||
(flow-m
|
||||
"(flow/start (sequence (flow-until (lambda (x) (> x 100)) (lambda (x) (* x 3)) 100) (lambda (x) (- x 100))) 5)")
|
||||
35)
|
||||
|
||||
(define flow-cmb-tests-run! (fn () {:total (+ flow-cmb-pass flow-cmb-fail) :passed flow-cmb-pass :failed flow-cmb-fail :fails flow-cmb-fails}))
|
||||
|
||||
@@ -16,7 +16,7 @@ federation extension via fed-sx for remote-node execution.
|
||||
|
||||
## Status (rolling)
|
||||
|
||||
`bash lib/flow/conformance.sh` → **116/116** (Phases 1-5 complete)
|
||||
`bash lib/flow/conformance.sh` → **122/122** (Phases 1-5 complete)
|
||||
|
||||
## Ground rules
|
||||
|
||||
@@ -145,7 +145,9 @@ something operators and authors actually use. Accumulation, not a rewrite.
|
||||
- [x] `recover` — complement to try-catch for the fail-VALUE channel: run node; if it
|
||||
yields `(fail ...)`, run a recovery node on the reason
|
||||
- [x] `map-flow` — run a flow per item of a list, join results (sequential)
|
||||
- [x] `lib/flow/tests/api.sx` (12) + `lib/flow/tests/combinators.sx` (11)
|
||||
- [x] `flow-while` / `flow-until` — bounded iteration: re-run body threading the
|
||||
value while/until pred holds, capped at `max` steps (deterministic bound)
|
||||
- [x] `lib/flow/tests/api.sx` (12) + `lib/flow/tests/combinators.sx` (17)
|
||||
|
||||
## Progress log
|
||||
|
||||
|
||||
Reference in New Issue
Block a user