Files
rose-ash/lib/host/execute.sx
giles ed68b9883d host: execute-fold — universality proven with a second fold (composition step 7)
The keystone validation of the universal-algebra thesis. lib/host/execute.sx is a SECOND
interpreter over the SAME seq/alt/each composition algebra as the render-fold — but a
different fold: leaves are EFFECTS, seq = steps in order, alt+when = branch, each =
for-each, and the accumulator is an effect log instead of an HTML string. It REUSES
compose.sx's shared machinery verbatim — host/comp--pred? (when), host/comp--field
(field/value), host/comp--source (each source) — so the predicate set, context-environment,
and iteration source are domain-agnostic; only the leaf semantics + accumulator are new.

KEYSTONE (tested): ONE (alt (when (has "auth") …) …) skeleton + ONE context folds two ways
— render picks the branch → "<b>in</b>", execute picks the SAME branch → {:verb "enter"}.
A publish workflow (validate → branch-on-status → notify-each) runs as one execute-fold over
a composition object. So the behaviour model (Slice 9) is "an execute-fold over a composition
object", not a separate system — the way the recursive tree proved recursion, this proves the
algebra is domain-agnostic. host/exec-run; 13/13 (new execute suite); wired into conformance
+ serve. Full host conformance 371/373 in 42s (warm); the 2 fails are the pre-existing
relate-picker pair.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 23:49:41 +00:00

77 lines
3.9 KiB
Plaintext

;; lib/host/execute.sx — the EXECUTE-fold: a SECOND interpreter over the SAME composition
;; algebra (seq/alt/each) as the render-fold (lib/host/compose.sx), proving the algebra is
;; domain-agnostic (plans/composition-objects.md step 7 — "prove universality with a second
;; fold"). What changes between folds is only what the combinators + leaves MEAN:
;;
;; domain fold seq alt+when each leaf
;; content render -> block order choose map items markup -> HTML string
;; behaviour execute -> steps in order branch for-each effect -> effect log
;;
;; Crucially this REUSES compose.sx's shared machinery — the `when` predicate set
;; (host/comp--pred?), the field/value resolver (host/comp--field), and the `each` source
;; (host/comp--source). So the predicate set, the context-environment, and the iteration
;; source are domain-agnostic; ONLY the leaf semantics (effect vs markup) and the fold's
;; accumulator (a list of effects vs a string) are new. The behaviour model (Slice 9) is
;; therefore "an execute-fold over a composition object", not a separate system.
;; resolve an effect argument against the context: (field K) reads the :item/ctx value via
;; the SAME resolver the render-fold uses; anything else is a literal.
(define host/exec--arg
(fn (a ctx)
(if (and (= (type-of a) "list") (= (str (first a)) "field"))
(host/comp--field (first (rest a)) ctx)
a)))
;; a leaf effect: (effect VERB ARG…) -> one effect record {:verb :args}. The execute-fold's
;; analogue of a render leaf — it performs (records) an effect rather than emitting markup.
(define host/exec--effect
(fn (verb args ctx)
(list {:verb (str verb) :args (map (fn (a) (host/exec--arg a ctx)) args)})))
;; seq: run every step IN ORDER, concatenating their effects (the sequential strategy).
(define host/exec--run-all
(fn (nodes ctx) (reduce (fn (acc n) (concat acc (host/exec--run n ctx))) (list) nodes)))
;; alt: BRANCH — run the FIRST branch whose `when` holds (reusing the render-fold's
;; predicate host/comp--pred?), else `else`. This is if/cond for the behaviour domain.
(define host/exec--alt
(fn (branches ctx)
(if (empty? branches)
(list)
(let ((br (first branches)) (bh (str (first (first branches)))))
(cond
((= bh "else") (host/exec--run (first (rest br)) ctx))
((= bh "when") (if (host/comp--pred? (first (rest br)) ctx)
(host/exec--run (first (rest (rest br))) ctx)
(host/exec--alt (rest branches) ctx)))
(else (host/exec--alt (rest branches) ctx)))))))
;; each: FOR-EACH — run the body per item from the (reused) source, :item bound, in order;
;; depth guard backstops runaway recursion, same as the render-fold.
(define host/exec--each
(fn (src body ctx)
(let ((depth (or (get ctx "depth") 0)))
(if (> depth 40)
(list {:verb "max-depth" :args (list)})
(reduce
(fn (acc item)
(concat acc (host/exec--run body (merge ctx {"item" item "depth" (+ depth 1)}))))
(list) (host/comp--source src ctx))))))
;; the execute-fold (the interpreter): same combinator dispatch shape as host/comp--render,
;; but leaves are effects and the accumulator is an effect log.
(define host/exec--run
(fn (node ctx)
(if (not (= (type-of node) "list"))
(list)
(let ((h (str (first node))) (args (rest node)))
(cond
((= h "seq") (host/exec--run-all args ctx))
((= h "alt") (host/exec--alt args ctx))
((= h "each") (host/exec--each (first args) (first (rest args)) ctx))
((= h "effect") (host/exec--effect (first args) (rest args) ctx))
(else (list)))))))
;; public entry: execute a composition node against a context -> the effect log (the run).
(define host/exec-run (fn (node ctx) (host/exec--run node ctx)))