flow: branch combinator (conditional) + 6 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 50s

Phase 2 control flow. (branch pred then else) selects then/else node by running
pred on the threaded input; named 'branch' since 'cond' is a Scheme special form.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 16:32:37 +00:00
parent 91ffba9975
commit 65cbdb8387
6 changed files with 77 additions and 11 deletions

View File

@@ -22,6 +22,7 @@ VERBOSE="${1:-}"
# Suites: NAME RUNNER-FN PATH # Suites: NAME RUNNER-FN PATH
SUITES=( SUITES=(
"basic flow-basic-tests-run! lib/flow/tests/basic.sx" "basic flow-basic-tests-run! lib/flow/tests/basic.sx"
"control flow-ctl-tests-run! lib/flow/tests/control.sx"
) )
TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT

View File

@@ -1,11 +1,13 @@
{ {
"total": 18, "total": 24,
"passed": 18, "passed": 24,
"failed": 0, "failed": 0,
"suites": { "suites": {
"basic": { "passed": 18, "total": 18 } "basic": { "passed": 18, "total": 18 },
"control": { "passed": 6, "total": 6 }
}, },
"phases": { "phases": {
"phase1": "in-progress" "phase1": "done",
"phase2": "in-progress"
} }
} }

View File

@@ -1,6 +1,6 @@
# flow-on-sx Scoreboard # flow-on-sx Scoreboard
**All tests pass: 18 / 18 across 1 suite.** **All tests pass: 24 / 24 across 2 suites.**
`bash lib/flow/conformance.sh` `bash lib/flow/conformance.sh`
@@ -9,6 +9,7 @@
| Suite | Passing | Covers | | Suite | Passing | Covers |
|-------|--------:|--------| |-------|--------:|--------|
| basic | 18 | Phase 1: single nodes, linear sequence, data-flow threading, defflow, parallel fan/join, nested composition, publish-shaped flow | | basic | 18 | Phase 1: single nodes, linear sequence, data-flow threading, defflow, parallel fan/join, nested composition, publish-shaped flow |
| control | 6 | Phase 2: `branch` conditional — true/false select, threaded predicate, full-node branches, nested 3-way, approval gate |
## Architecture ## Architecture
@@ -34,8 +35,7 @@ capture the flow continuation directly.
## Phases ## Phases
- [~] Phase 1 — Declarative DAG + sequential execution (combinators + 18 tests done; - [x] Phase 1 — Declarative DAG + sequential execution (combinators + 18 tests, `flow/start`)
`flow/start` done) - [~] Phase 2 — Control flow + error handling (`branch` done)
- [ ] Phase 2 — Control flow + error handling
- [ ] Phase 3 — Suspend / resume (the showcase) - [ ] Phase 3 — Suspend / resume (the showcase)
- [ ] Phase 4 — Distributed nodes via fed-sx - [ ] Phase 4 — Distributed nodes via fed-sx

View File

@@ -7,7 +7,7 @@
;; Scheme interpreter so that Phase 3's `suspend` (call/cc) can capture the ;; Scheme interpreter so that Phase 3's `suspend` (call/cc) can capture the
;; flow continuation directly. ;; flow continuation directly.
;; ;;
;; Phase 1 combinators: ;; Phase 1 combinators (flow-combinators-src):
;; (flow-node f) — wrap a 1-arg procedure as a node (identity) ;; (flow-node f) — wrap a 1-arg procedure as a node (identity)
;; (flow-id input) — pass the upstream value through unchanged ;; (flow-id input) — pass the upstream value through unchanged
;; (flow-const v) — node that ignores input and yields v ;; (flow-const v) — node that ignores input and yields v
@@ -15,15 +15,24 @@
;; (parallel n ...) — fan input to every child, join results into a list ;; (parallel n ...) — fan input to every child, join results into a list
;; (SEQUENTIAL evaluation; true concurrency is Phase 3) ;; (SEQUENTIAL evaluation; true concurrency is Phase 3)
;; (defflow name body)— bind a named flow ;; (defflow name body)— bind a named flow
;;
;; Phase 2 combinators (flow-control-src):
;; (branch pred then else) — pred on input selects then/else node
;; (`cond` is a Scheme special form, so the combinator is named `branch`)
(define (define
flow-combinators-src 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-syntax defflow\n (syntax-rules ()\n ((defflow nm body) (define nm body))))") "(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-syntax defflow\n (syntax-rules ()\n ((defflow nm body) (define nm body))))")
(define
flow-control-src
"(define (branch pred then else)\n (lambda (input) (if (pred input) (then input) (else input))))")
(define (define
flow-load-combinators! flow-load-combinators!
(fn (fn
(env) (env)
(begin (begin
(scheme-eval-program (scheme-parse-all flow-combinators-src) env) (scheme-eval-program (scheme-parse-all flow-combinators-src) env)
(scheme-eval-program (scheme-parse-all flow-control-src) env)
env))) env)))

53
lib/flow/tests/control.sx Normal file
View File

@@ -0,0 +1,53 @@
;; lib/flow/tests/control.sx — Phase 2: control flow + error handling.
(define flow-ctl-pass 0)
(define flow-ctl-fail 0)
(define flow-ctl-fails (list))
(define
flow-ctl-test
(fn
(name actual expected)
(if
(= actual expected)
(set! flow-ctl-pass (+ flow-ctl-pass 1))
(begin
(set! flow-ctl-fail (+ flow-ctl-fail 1))
(append! flow-ctl-fails {:name name :expected expected :actual actual})))))
(define flow-c (fn (src) (flow-run src)))
(define flow-cs (fn (src) (get (flow-run src) :scm-string)))
;; ── branch ──────────────────────────────────────────────────────
(flow-ctl-test
"branch: true selects then"
(flow-c
"(flow/start (branch (lambda (x) (> x 0)) (lambda (x) (* x 100)) (lambda (x) (- 0 x))) 5)")
500)
(flow-ctl-test
"branch: false selects else"
(flow-c
"(flow/start (branch (lambda (x) (> x 0)) (lambda (x) (* x 100)) (lambda (x) (- 0 x))) -3)")
3)
(flow-ctl-test
"branch: predicate sees the threaded input"
(flow-c
"(flow/start (sequence (lambda (x) (+ x 1)) (branch (lambda (x) (> x 3)) (flow-const 100) (flow-const 0))) 3)")
100)
(flow-ctl-test
"branch: branches are full nodes (sequence inside)"
(flow-c
"(flow/start (branch (lambda (x) (< x 10)) (sequence (lambda (x) (+ x 1)) (lambda (x) (* x 2))) (flow-const 0)) 4)")
10)
(flow-ctl-test
"branch: nested branch (3-way sign)"
(flow-c
"(defflow sign (branch (lambda (x) (> x 0)) (flow-const 1) (branch (lambda (x) (< x 0)) (flow-const -1) (flow-const 0)))) (list (flow/start sign 7) (flow/start sign -7) (flow/start sign 0))")
(list 1 -1 0))
(flow-ctl-test
"branch: publish-shaped approval gate"
(flow-cs
"(defflow publish (branch (lambda (post) (>= (string-length post) 3)) (lambda (post) (string-append post \" [published]\")) (lambda (post) (string-append post \" [rejected]\")))) (flow/start publish \"ok\")")
"ok [rejected]")
(define flow-ctl-tests-run! (fn () {:total (+ flow-ctl-pass flow-ctl-fail) :passed flow-ctl-pass :failed flow-ctl-fail :fails flow-ctl-fails}))

View File

@@ -16,7 +16,7 @@ federation extension via fed-sx for remote-node execution.
## Status (rolling) ## Status (rolling)
`bash lib/flow/conformance.sh`**18/18** (Phase 1 in progress) `bash lib/flow/conformance.sh`**24/24** (Phase 1 done; Phase 2 in progress)
## Ground rules ## Ground rules
@@ -75,7 +75,8 @@ lib/flow/spec.sx lib/flow/runtime.sx lib/flow/store.sx
## Phase 2 — Control flow + error handling ## Phase 2 — Control flow + error handling
- [ ] `cond` combinator — predicate selects branch - [x] `cond` combinator — predicate selects branch (named `branch`; `cond` is a
Scheme special form). `(branch pred then else)` — 6 tests.
- [ ] `retry n [backoff]` — re-runs node up to n times on exception - [ ] `retry n [backoff]` — re-runs node up to n times on exception
- [ ] `timeout ms` — bounds node execution - [ ] `timeout ms` — bounds node execution
- [ ] `try-catch` — exception handler with reified error - [ ] `try-catch` — exception handler with reified error