From c6a662c98078c72d9ba7a8b6ce28331cf9aa5c0d Mon Sep 17 00:00:00 2001 From: giles Date: Sun, 15 Mar 2026 14:10:33 +0000 Subject: [PATCH] Phase 4: Eliminate nested CEK from HO form handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Higher-order forms (map, filter, reduce, some, every?, for-each, map-indexed) now evaluate their arguments via CEK frames instead of nested trampoline(eval-expr(...)) calls. Added HoSetupFrame — staged evaluation of HO form arguments. When all args are evaluated, ho-setup-dispatch sets up the iteration frame. This keeps a single linear CEK continuation chain instead of spawning nested CEK instances. 14 nested eval-expr calls eliminated (39 → 25 remaining). The remaining 25 are in delegate functions (sf-letrec, sf-scope, parse-keyword-args, qq-expand, etc.) called infrequently. All tests unchanged: JS 747/747, Full 864/870, Python 679/679. Co-Authored-By: Claude Opus 4.6 (1M context) --- shared/static/scripts/sx-browser.js | 84 +++--- spec/evaluator.sx | 142 ++++++--- sx/sx/nav-data.sx | 6 +- sx/sx/plans/mother-language.sx | 450 ++++++++++++++++++++++++++++ sx/sxc/pages/docs.sx | 2 + 5 files changed, 603 insertions(+), 81 deletions(-) create mode 100644 sx/sx/plans/mother-language.sx diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index 1d952ba..cbc95f5 100644 --- a/shared/static/scripts/sx-browser.js +++ b/shared/static/scripts/sx-browser.js @@ -14,7 +14,7 @@ // ========================================================================= var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); - var SX_VERSION = "2026-03-15T13:41:19Z"; + var SX_VERSION = "2026-03-15T14:09:57Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -941,6 +941,10 @@ PRIMITIVES["make-reactive-reset-frame"] = makeReactiveResetFrame; var makeDerefFrame = function(env) { return {"type": "deref", "env": env}; }; PRIMITIVES["make-deref-frame"] = makeDerefFrame; + // make-ho-setup-frame + var makeHoSetupFrame = function(hoType, remainingArgs, evaledArgs, env) { return {"type": "ho-setup", "ho-type": hoType, "remaining": remainingArgs, "evaled": evaledArgs, "env": env}; }; +PRIMITIVES["make-ho-setup-frame"] = makeHoSetupFrame; + // frame-type var frameType = function(f) { return get(f, "type"); }; PRIMITIVES["frame-type"] = frameType; @@ -1564,61 +1568,61 @@ PRIMITIVES["reactive-shift-deref"] = reactiveShiftDeref; })(); }; PRIMITIVES["step-eval-call"] = stepEvalCall; - // step-ho-map - var stepHoMap = function(args, env, kont) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); + // ho-setup-dispatch + var hoSetupDispatch = function(hoType, evaled, env, kont) { return (function() { + var f = first(evaled); + return (isSxTruthy((hoType == "map")) ? (function() { + var coll = nth(evaled, 1); return (isSxTruthy(isEmpty(coll)) ? makeCekValue([], env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeMapFrame(f, rest(coll), [], env), kont))); +})() : (isSxTruthy((hoType == "map-indexed")) ? (function() { + var coll = nth(evaled, 1); + return (isSxTruthy(isEmpty(coll)) ? makeCekValue([], env, kont) : continueWithCall(f, [0, first(coll)], env, [], kontPush(makeMapIndexedFrame(f, rest(coll), [], env), kont))); +})() : (isSxTruthy((hoType == "filter")) ? (function() { + var coll = nth(evaled, 1); + return (isSxTruthy(isEmpty(coll)) ? makeCekValue([], env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeFilterFrame(f, rest(coll), [], first(coll), env), kont))); +})() : (isSxTruthy((hoType == "reduce")) ? (function() { + var init = nth(evaled, 1); + var coll = nth(evaled, 2); + return (isSxTruthy(isEmpty(coll)) ? makeCekValue(init, env, kont) : continueWithCall(f, [init, first(coll)], env, [], kontPush(makeReduceFrame(f, rest(coll), env), kont))); +})() : (isSxTruthy((hoType == "some")) ? (function() { + var coll = nth(evaled, 1); + return (isSxTruthy(isEmpty(coll)) ? makeCekValue(false, env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeSomeFrame(f, rest(coll), env), kont))); +})() : (isSxTruthy((hoType == "every")) ? (function() { + var coll = nth(evaled, 1); + return (isSxTruthy(isEmpty(coll)) ? makeCekValue(true, env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeEveryFrame(f, rest(coll), env), kont))); +})() : (isSxTruthy((hoType == "for-each")) ? (function() { + var coll = nth(evaled, 1); + return (isSxTruthy(isEmpty(coll)) ? makeCekValue(NIL, env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeForEachFrame(f, rest(coll), env), kont))); +})() : error((String("Unknown HO type: ") + String(hoType)))))))))); })(); }; +PRIMITIVES["ho-setup-dispatch"] = hoSetupDispatch; + + // step-ho-map + var stepHoMap = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeHoSetupFrame("map", rest(args), [], env), kont)); }; PRIMITIVES["step-ho-map"] = stepHoMap; // step-ho-map-indexed - var stepHoMapIndexed = function(args, env, kont) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return (isSxTruthy(isEmpty(coll)) ? makeCekValue([], env, kont) : continueWithCall(f, [0, first(coll)], env, [], kontPush(makeMapIndexedFrame(f, rest(coll), [], env), kont))); -})(); }; + var stepHoMapIndexed = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeHoSetupFrame("map-indexed", rest(args), [], env), kont)); }; PRIMITIVES["step-ho-map-indexed"] = stepHoMapIndexed; // step-ho-filter - var stepHoFilter = function(args, env, kont) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return (isSxTruthy(isEmpty(coll)) ? makeCekValue([], env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeFilterFrame(f, rest(coll), [], first(coll), env), kont))); -})(); }; + var stepHoFilter = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeHoSetupFrame("filter", rest(args), [], env), kont)); }; PRIMITIVES["step-ho-filter"] = stepHoFilter; // step-ho-reduce - var stepHoReduce = function(args, env, kont) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var init = trampoline(evalExpr(nth(args, 1), env)); - var coll = trampoline(evalExpr(nth(args, 2), env)); - return (isSxTruthy(isEmpty(coll)) ? makeCekValue(init, env, kont) : continueWithCall(f, [init, first(coll)], env, [], kontPush(makeReduceFrame(f, rest(coll), env), kont))); -})(); }; + var stepHoReduce = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeHoSetupFrame("reduce", rest(args), [], env), kont)); }; PRIMITIVES["step-ho-reduce"] = stepHoReduce; // step-ho-some - var stepHoSome = function(args, env, kont) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return (isSxTruthy(isEmpty(coll)) ? makeCekValue(false, env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeSomeFrame(f, rest(coll), env), kont))); -})(); }; + var stepHoSome = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeHoSetupFrame("some", rest(args), [], env), kont)); }; PRIMITIVES["step-ho-some"] = stepHoSome; // step-ho-every - var stepHoEvery = function(args, env, kont) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return (isSxTruthy(isEmpty(coll)) ? makeCekValue(true, env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeEveryFrame(f, rest(coll), env), kont))); -})(); }; + var stepHoEvery = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeHoSetupFrame("every", rest(args), [], env), kont)); }; PRIMITIVES["step-ho-every"] = stepHoEvery; // step-ho-for-each - var stepHoForEach = function(args, env, kont) { return (function() { - var f = trampoline(evalExpr(first(args), env)); - var coll = trampoline(evalExpr(nth(args, 1), env)); - return (isSxTruthy(isEmpty(coll)) ? makeCekValue(NIL, env, kont) : continueWithCall(f, [first(coll)], env, [], kontPush(makeForEachFrame(f, rest(coll), env), kont))); -})(); }; + var stepHoForEach = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeHoSetupFrame("for-each", rest(args), [], env), kont)); }; PRIMITIVES["step-ho-for-each"] = stepHoForEach; // step-continue @@ -1747,6 +1751,12 @@ PRIMITIVES["step-ho-for-each"] = stepHoForEach; return makeCekState(nth(nextEntry, 1), fenv, kontPush(makeDictFrame(rest(remaining), append(completed, [[first(nextEntry)]]), fenv), restK)); })()); })(); +})() : (isSxTruthy((ft == "ho-setup")) ? (function() { + var hoType = get(frame, "ho-type"); + var remaining = get(frame, "remaining"); + var evaled = append(get(frame, "evaled"), [value]); + var fenv = get(frame, "env"); + return (isSxTruthy(isEmpty(remaining)) ? hoSetupDispatch(hoType, evaled, fenv, restK) : makeCekState(first(remaining), fenv, kontPush(makeHoSetupFrame(hoType, rest(remaining), evaled, fenv), restK))); })() : (isSxTruthy((ft == "reset")) ? makeCekValue(value, env, restK) : (isSxTruthy((ft == "deref")) ? (function() { var val = value; var fenv = get(frame, "env"); @@ -1814,7 +1824,7 @@ PRIMITIVES["step-ho-for-each"] = stepHoForEach; var remaining = get(frame, "remaining"); var fenv = get(frame, "env"); return (isSxTruthy(!isSxTruthy(value)) ? makeCekValue(false, fenv, restK) : (isSxTruthy(isEmpty(remaining)) ? makeCekValue(true, fenv, restK) : continueWithCall(f, [first(remaining)], fenv, [], kontPush(makeEveryFrame(f, rest(remaining), fenv), restK)))); -})() : error((String("Unknown frame type: ") + String(ft)))))))))))))))))))))))))); +})() : error((String("Unknown frame type: ") + String(ft))))))))))))))))))))))))))); })()); })(); }; PRIMITIVES["step-continue"] = stepContinue; diff --git a/spec/evaluator.sx b/spec/evaluator.sx index 3515576..3c2ad89 100644 --- a/spec/evaluator.sx +++ b/spec/evaluator.sx @@ -197,6 +197,14 @@ (fn (env) {:type "deref" :env env})) +;; HoSetupFrame: staged evaluation of higher-order form arguments +;; ho-type is "map", "filter", "reduce", etc. +;; Evaluates args one at a time, then dispatches to the iteration frame. +(define make-ho-setup-frame + (fn (ho-type remaining-args evaled-args env) + {:type "ho-setup" :ho-type ho-type :remaining remaining-args + :evaled evaled-args :env env})) + ;; -------------------------------------------------------------------------- ;; 3. Frame accessors @@ -1571,69 +1579,101 @@ ;; Function and collection args are evaluated via tree-walk (simple exprs), ;; then the loop is driven by CEK frames. +;; HO step handlers — push HoSetupFrame to evaluate args via CEK +;; (no nested eval-expr calls). When all args are evaluated, the +;; HoSetupFrame dispatch in step-continue sets up the iteration frame. + +;; ho-setup-dispatch: all HO args evaluated, set up iteration +(define ho-setup-dispatch + (fn (ho-type evaled env kont) + (let ((f (first evaled))) + (cond + (= ho-type "map") + (let ((coll (nth evaled 1))) + (if (empty? coll) + (make-cek-value (list) env kont) + (continue-with-call f (list (first coll)) env (list) + (kont-push (make-map-frame f (rest coll) (list) env) kont)))) + + (= ho-type "map-indexed") + (let ((coll (nth evaled 1))) + (if (empty? coll) + (make-cek-value (list) env kont) + (continue-with-call f (list 0 (first coll)) env (list) + (kont-push (make-map-indexed-frame f (rest coll) (list) env) kont)))) + + (= ho-type "filter") + (let ((coll (nth evaled 1))) + (if (empty? coll) + (make-cek-value (list) env kont) + (continue-with-call f (list (first coll)) env (list) + (kont-push (make-filter-frame f (rest coll) (list) (first coll) env) kont)))) + + (= ho-type "reduce") + (let ((init (nth evaled 1)) + (coll (nth evaled 2))) + (if (empty? coll) + (make-cek-value init env kont) + (continue-with-call f (list init (first coll)) env (list) + (kont-push (make-reduce-frame f (rest coll) env) kont)))) + + (= ho-type "some") + (let ((coll (nth evaled 1))) + (if (empty? coll) + (make-cek-value false env kont) + (continue-with-call f (list (first coll)) env (list) + (kont-push (make-some-frame f (rest coll) env) kont)))) + + (= ho-type "every") + (let ((coll (nth evaled 1))) + (if (empty? coll) + (make-cek-value true env kont) + (continue-with-call f (list (first coll)) env (list) + (kont-push (make-every-frame f (rest coll) env) kont)))) + + (= ho-type "for-each") + (let ((coll (nth evaled 1))) + (if (empty? coll) + (make-cek-value nil env kont) + (continue-with-call f (list (first coll)) env (list) + (kont-push (make-for-each-frame f (rest coll) env) kont)))) + + :else (error (str "Unknown HO type: " ho-type)))))) + (define step-ho-map (fn (args env kont) - (let ((f (trampoline (eval-expr (first args) env))) - (coll (trampoline (eval-expr (nth args 1) env)))) - (if (empty? coll) - (make-cek-value (list) env kont) - (continue-with-call f (list (first coll)) env (list) - (kont-push (make-map-frame f (rest coll) (list) env) kont)))))) + (make-cek-state (first args) env + (kont-push (make-ho-setup-frame "map" (rest args) (list) env) kont)))) (define step-ho-map-indexed (fn (args env kont) - (let ((f (trampoline (eval-expr (first args) env))) - (coll (trampoline (eval-expr (nth args 1) env)))) - (if (empty? coll) - (make-cek-value (list) env kont) - (continue-with-call f (list 0 (first coll)) env (list) - (kont-push (make-map-indexed-frame f (rest coll) (list) env) kont)))))) + (make-cek-state (first args) env + (kont-push (make-ho-setup-frame "map-indexed" (rest args) (list) env) kont)))) (define step-ho-filter (fn (args env kont) - (let ((f (trampoline (eval-expr (first args) env))) - (coll (trampoline (eval-expr (nth args 1) env)))) - (if (empty? coll) - (make-cek-value (list) env kont) - (continue-with-call f (list (first coll)) env (list) - (kont-push (make-filter-frame f (rest coll) (list) (first coll) env) kont)))))) + (make-cek-state (first args) env + (kont-push (make-ho-setup-frame "filter" (rest args) (list) env) kont)))) (define step-ho-reduce (fn (args env kont) - (let ((f (trampoline (eval-expr (first args) env))) - (init (trampoline (eval-expr (nth args 1) env))) - (coll (trampoline (eval-expr (nth args 2) env)))) - (if (empty? coll) - (make-cek-value init env kont) - (continue-with-call f (list init (first coll)) env (list) - (kont-push (make-reduce-frame f (rest coll) env) kont)))))) + (make-cek-state (first args) env + (kont-push (make-ho-setup-frame "reduce" (rest args) (list) env) kont)))) (define step-ho-some (fn (args env kont) - (let ((f (trampoline (eval-expr (first args) env))) - (coll (trampoline (eval-expr (nth args 1) env)))) - (if (empty? coll) - (make-cek-value false env kont) - (continue-with-call f (list (first coll)) env (list) - (kont-push (make-some-frame f (rest coll) env) kont)))))) + (make-cek-state (first args) env + (kont-push (make-ho-setup-frame "some" (rest args) (list) env) kont)))) (define step-ho-every (fn (args env kont) - (let ((f (trampoline (eval-expr (first args) env))) - (coll (trampoline (eval-expr (nth args 1) env)))) - (if (empty? coll) - (make-cek-value true env kont) - (continue-with-call f (list (first coll)) env (list) - (kont-push (make-every-frame f (rest coll) env) kont)))))) + (make-cek-state (first args) env + (kont-push (make-ho-setup-frame "every" (rest args) (list) env) kont)))) (define step-ho-for-each (fn (args env kont) - (let ((f (trampoline (eval-expr (first args) env))) - (coll (trampoline (eval-expr (nth args 1) env)))) - (if (empty? coll) - (make-cek-value nil env kont) - (continue-with-call f (list (first coll)) env (list) - (kont-push (make-for-each-frame f (rest coll) env) kont)))))) + (make-cek-state (first args) env + (kont-push (make-ho-setup-frame "for-each" (rest args) (list) env) kont)))) ;; -------------------------------------------------------------------------- @@ -1908,6 +1948,22 @@ fenv) rest-k)))))) + ;; --- HoSetupFrame: evaluating HO form arguments --- + (= ft "ho-setup") + (let ((ho-type (get frame "ho-type")) + (remaining (get frame "remaining")) + (evaled (append (get frame "evaled") (list value))) + (fenv (get frame "env"))) + (if (empty? remaining) + ;; All args evaluated — dispatch to iteration + (ho-setup-dispatch ho-type evaled fenv rest-k) + ;; More args to evaluate + (make-cek-state + (first remaining) fenv + (kont-push + (make-ho-setup-frame ho-type (rest remaining) evaled fenv) + rest-k)))) + ;; --- ResetFrame: body evaluated normally (no shift) --- (= ft "reset") (make-cek-value value env rest-k) diff --git a/sx/sx/nav-data.sx b/sx/sx/nav-data.sx index 9f22763..7bfbfe5 100644 --- a/sx/sx/nav-data.sx +++ b/sx/sx/nav-data.sx @@ -246,7 +246,11 @@ (dict :label "Reactive Runtime" :href "/sx/(etc.(plan.reactive-runtime))" :summary "Seven feature layers — ref, foreign FFI, state machines, commands with undo/redo, render loops, keyed lists, client-first app shell. Zero new platform primitives.") (dict :label "Rust/WASM Host" :href "/sx/(etc.(plan.rust-wasm-host))" - :summary "Bootstrap the SX spec to Rust, compile to WASM, replace sx-browser.js. Shared platform layer for DOM, phased rollout from parse to full parity."))) + :summary "Bootstrap the SX spec to Rust, compile to WASM, replace sx-browser.js. Shared platform layer for DOM, phased rollout from parse to full parity.") + (dict :label "Isolated Evaluator" :href "/sx/(etc.(plan.isolated-evaluator))" + :summary "Core/application split, shared sx-platform.js, isolated JS evaluator, Rust WASM via handle table. Only language-defining spec gets bootstrapped; everything else is runtime-evaluated .sx.") + (dict :label "Mother Language" :href "/sx/(etc.(plan.mother-language))" + :summary "SX as its own compiler. OCaml as substrate (closest to CEK), Koka as alternative (compile-time linearity), ultimately self-hosting. One language, every target."))) (define reactive-islands-nav-items (list (dict :label "Overview" :href "/sx/(geography.(reactive))" diff --git a/sx/sx/plans/mother-language.sx b/sx/sx/plans/mother-language.sx new file mode 100644 index 0000000..0704531 --- /dev/null +++ b/sx/sx/plans/mother-language.sx @@ -0,0 +1,450 @@ +;; --------------------------------------------------------------------------- +;; Mother Language — SX as its own compiler, OCaml as the substrate +;; --------------------------------------------------------------------------- + +(defcomp ~plans/mother-language/plan-mother-language-content () + (~docs/page :title "Mother Language" + + (p :class "text-stone-500 text-sm italic mb-8" + "The ideal language for evaluating the SX core spec is SX itself. " + "The path: OCaml as the initial substrate (closest existing language to what CEK is), " + "Koka as an alternative (compile-time linearity), ultimately a self-hosting SX compiler " + "that emits machine code directly from the spec. One language. Every target.") + + + ;; ----------------------------------------------------------------------- + ;; The argument + ;; ----------------------------------------------------------------------- + + (~docs/section :title "The Argument" :id "argument" + + (h4 :class "font-semibold mt-4 mb-2" "What the evaluator actually does") + (p "The CEK machine is a " (code "state \u2192 state") " loop over sum types. " + "Each step pattern-matches on the Control register, consults the Environment, " + "and transforms the Kontinuation. Every SX expression, every component render, " + "every signal update goes through this loop. It's the hot path.") + (p "The ideal host language is one where this loop compiles to a tight jump table " + "with minimal allocation. That means: algebraic types, pattern matching, " + "persistent data structures, and a native effect system.") + + (h4 :class "font-semibold mt-6 mb-2" "Why multiple hosts is the wrong goal") + (p "The current architecture bootstraps the spec to Python, JavaScript, and Rust. " + "Each host has impedance mismatches:") + (ul :class "list-disc list-inside space-y-1 mt-2" + (li (strong "Python") " \u2014 slow. Tree-walk overhead is 100\u20131000x vs native. " + "The async adapter is complex because Python's async model is cooperative, not effect-based.") + (li (strong "JavaScript") " \u2014 no sum types, prototype-based dispatch, GC pauses unpredictable. " + "The bootstrapper works around JS limitations rather than mapping naturally.") + (li (strong "Rust") " \u2014 ownership fights shared immutable trees. Every " (code "Rc>") + " is overhead. Closures capturing environments need " (code "Arc") " gymnastics.")) + (p "Each host makes the evaluator work, but none make it " (em "natural") ". " + "The translation is structure-" (em "creating") ", not structure-" (em "preserving") ".") + + (h4 :class "font-semibold mt-6 mb-2" "The Mother Language is SX") + (p "The spec defines the semantics. The CEK machine is the most explicit form of those semantics. " + "The ideal \"language\" is one that maps 1:1 onto CEK transitions and compiles them to " + "optimal machine code. That language is SX itself \u2014 compiled, not interpreted.") + (p "The hosts (Python, JS, Haskell, Rust) become " (em "platform layers") " \u2014 " + "they provide IO, DOM, database, GPU access. The evaluator itself is always the same " + "compiled SX core, embedded as a native library or WASM module.")) + + + ;; ----------------------------------------------------------------------- + ;; Why OCaml + ;; ----------------------------------------------------------------------- + + (~docs/section :title "Why OCaml" :id "ocaml" + + (p "OCaml is the closest existing language to what the CEK machine is. " + "The translation from " (code "cek.sx") " to OCaml is nearly mechanical.") + + (h4 :class "font-semibold mt-4 mb-2" "Natural mapping") + (div :class "overflow-x-auto rounded border border-stone-200 mb-4" + (table :class "w-full text-left text-sm" + (thead (tr :class "border-b border-stone-200 bg-stone-100" + (th :class "px-3 py-2 font-medium text-stone-600" "SX concept") + (th :class "px-3 py-2 font-medium text-stone-600" "OCaml primitive") + (th :class "px-3 py-2 font-medium text-stone-600" "Notes"))) + (tbody + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" "Value (Nil | Num | Str | List | ...)") + (td :class "px-3 py-2 text-stone-700" "Algebraic variant type") + (td :class "px-3 py-2 text-stone-600" "Direct mapping, no boxing")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" "Frame (IfFrame | ArgFrame | MapFrame | ...)") + (td :class "px-3 py-2 text-stone-700" "Algebraic variant type") + (td :class "px-3 py-2 text-stone-600" "20+ variants, pattern match dispatch")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" "Environment (persistent map)") + (td :class "px-3 py-2 text-stone-700" (code "Map.S")) + (td :class "px-3 py-2 text-stone-600" "Built-in balanced tree, structural sharing")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" "Continuation (list of frames)") + (td :class "px-3 py-2 text-stone-700" "Immutable list") + (td :class "px-3 py-2 text-stone-600" "cons/match, O(1) push/pop")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" "cek-step (pattern match on C)") + (td :class "px-3 py-2 text-stone-700" (code "match") " expression") + (td :class "px-3 py-2 text-stone-600" "Compiles to jump table")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" "shift/reset") + (td :class "px-3 py-2 text-stone-700" (code "perform") " / " (code "continue")) + (td :class "px-3 py-2 text-stone-600" "Native in OCaml 5")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" "Concurrent CEK (fibers)") + (td :class "px-3 py-2 text-stone-700" "Domains + effect handlers") + (td :class "px-3 py-2 text-stone-600" "One fiber per CEK machine")) + (tr + (td :class "px-3 py-2 text-stone-700" "Linear continuations") + (td :class "px-3 py-2 text-stone-700" "One-shot continuations (default)") + (td :class "px-3 py-2 text-stone-600" "Runtime-enforced, not compile-time"))))) + + (h4 :class "font-semibold mt-4 mb-2" "Compilation targets") + (ul :class "list-disc list-inside space-y-1 mt-2" + (li (strong "Native") " \u2014 OCaml's native compiler produces fast binaries, small footprint. " + "Embed in Python via C ABI (ctypes/cffi). Embed in Node via N-API.") + (li (strong "WASM") " \u2014 " (code "wasm_of_ocaml") " is mature (used by Facebook's Flow/Reason). " + "Produces compact " (code ".wasm") " modules. Loaded via " (code "sx-platform.js") " like the Rust WASM plan.") + (li (strong "JavaScript") " \u2014 " (code "js_of_ocaml") " for legacy browser targets. " + "Falls back to JS when WASM isn't available.")) + + (h4 :class "font-semibold mt-4 mb-2" "What OCaml replaces") + (p "The Haskell and Rust evaluator implementations become unnecessary. " + "OCaml covers both server (native) and client (WASM) from one codebase. " + "The sx-haskell and sx-rust work proved the spec is host-independent \u2014 " + "OCaml is the convergence point.") + (p "Python and JavaScript evaluators remain as " (em "platform layers") " \u2014 " + "they provide IO primitives, not evaluation logic. The Python web framework calls " + "the OCaml evaluator via FFI for rendering. The browser loads the WASM evaluator " + "and connects it to " (code "sx-platform.js") " for DOM access.")) + + + ;; ----------------------------------------------------------------------- + ;; Koka as alternative + ;; ----------------------------------------------------------------------- + + (~docs/section :title "Koka as Alternative" :id "koka" + + (p "Koka (Daan Leijen, MSR) addresses OCaml's one weakness: " + (strong "compile-time linearity") ".") + + (h4 :class "font-semibold mt-4 mb-2" "Where Koka wins") + (ul :class "list-disc list-inside space-y-2 mt-2" + (li (strong "Perceus reference counting") " \u2014 the compiler tracks which values are used linearly. " + "Linear values are mutated in-place (zero allocation). " + "Non-linear values use reference counting (no GC at all).") + (li (strong "Effect types") " \u2014 effects are tracked in the type system. " + "A function's type says exactly which effects it can perform. " + "The type checker enforces that handlers exist for every effect.") + (li (strong "One-shot continuations by default") " \u2014 like OCaml 5, but " (em "statically enforced") ". " + "The type system prevents invoking a linear continuation twice. " + "No runtime check needed.")) + + (h4 :class "font-semibold mt-4 mb-2" "Where Koka is weaker") + (ul :class "list-disc list-inside space-y-2 mt-2" + (li (strong "Maturity") " \u2014 research language. Smaller ecosystem, fewer FFI bindings, " + "less battle-tested than OCaml.") + (li (strong "WASM backend") " \u2014 compiles to C \u2192 WASM (via Emscripten). " + "Works but less optimized than " (code "wasm_of_ocaml") " or Rust's " (code "wasm-pack") ".") + (li (strong "Embeddability") " \u2014 C FFI works but less established for Python/Node embedding.")) + + (p "Koka is the right choice " (em "if") " compile-time linearity proves essential for correctness. " + "The decision point is Step 5 (Linear Effects) of the foundations plan. " + "If SX's own type system can enforce linearity at spec-validation time, " + "OCaml's runtime enforcement is sufficient. If not, Koka becomes the better substrate.")) + + + ;; ----------------------------------------------------------------------- + ;; The path to self-hosting + ;; ----------------------------------------------------------------------- + + (~docs/section :title "The Path to Self-Hosting" :id "self-hosting" + + (p "The end state: SX compiles itself. No intermediate language, no general-purpose host.") + + (h4 :class "font-semibold mt-4 mb-2" "Phase 1: OCaml bootstrapper") + (p "Write " (code "bootstrap_ml.py") " \u2014 reads " (code "cek.sx") " + " (code "frames.sx") + " + " (code "primitives.sx") " + " (code "eval.sx") ", emits OCaml source. " + "Same pattern as the existing Rust/Python/JS bootstrappers.") + (p "The OCaml output is a standalone module:") + (~docs/code :code (highlight "type value =\n | Nil | Bool of bool | Num of float | Str of string\n | Sym of string | Kw of string\n | List of value list | Dict of (value * value) list\n | Lambda of params * value list * env\n | Component of string * params * value list * env\n | Handle of int (* opaque FFI reference *)\n\ntype frame =\n | IfFrame of value list * value list * env\n | ArgFrame of value list * value list * env\n | MapFrame of value * value list * value list * env\n | ReactiveResetFrame of value\n | DerefFrame of value\n (* ... 20+ frame types from frames.sx *)\n\ntype kont = frame list\ntype state = value * env * kont\n\nlet step ((ctrl, env, kont) : state) : state =\n match ctrl with\n | Lit v -> continue_val v kont\n | Var name -> continue_val (Env.find name env) kont\n | App (f, args) -> (f, env, ArgFrame(args, [], env) :: kont)\n | ..." "ocaml")) + + (h4 :class "font-semibold mt-6 mb-2" "Phase 2: Native + WASM builds") + (p "Compile the OCaml output to:") + (ul :class "list-disc list-inside space-y-1 mt-2" + (li (code "sx_core.so") " / " (code "sx_core.dylib") " \u2014 native shared library, C ABI") + (li (code "sx_core.wasm") " \u2014 via " (code "wasm_of_ocaml") " for browser") + (li (code "sx_core.js") " \u2014 via " (code "js_of_ocaml") " as JS fallback")) + (p "Python web framework calls " (code "sx_core.so") " via cffi. " + "Browser loads " (code "sx_core.wasm") " via " (code "sx-platform.js") ".") + + (h4 :class "font-semibold mt-6 mb-2" "Phase 3: SX evaluates web framework") + (p "The compiled core evaluator loads web framework " (code ".sx") " at runtime " + "(signals, engine, orchestration, boot). Same as the " + (a :href "/sx/(etc.(plan.isolated-evaluator))" "Isolated Evaluator") " plan, " + "but the evaluator is compiled OCaml/WASM instead of bootstrapped JS.") + + (h4 :class "font-semibold mt-6 mb-2" "Phase 4: SX linearity checking") + (p "Extend " (code "types.sx") " with quantity annotations:") + (~docs/code :code (highlight ";; Quantity annotations on types\n(define-type (Signal a) :quantity :affine) ;; use at most once per scope\n(define-type (Channel a) :quantity :linear) ;; must be consumed exactly once\n\n;; Effect declarations with linearity\n(define-io-primitive \"send-message\"\n :params (channel message)\n :quantity :linear\n :effects [io]\n :doc \"Must be handled exactly once.\")\n\n;; The type checker (specced in .sx, compiled to OCaml) validates\n;; linearity at component registration time. Runtime enforcement\n;; by OCaml's one-shot continuations is the safety net." "lisp")) + (p "The type checker runs at spec-validation time. The compiled evaluator " + "executes already-verified code. SX's type system provides the linearity " + "guarantees, not the host language.") + + (h4 :class "font-semibold mt-6 mb-2" "Phase 5: Self-hosting compiler") + (p "Write the compiler itself in SX:") + (ul :class "list-disc list-inside space-y-1 mt-2" + (li "Spec the CEK-to-native code generation in " (code ".sx") " files") + (li "The Phase 2 OCaml evaluator compiles the compiler spec") + (li "The compiled compiler can then compile itself") + (li "OCaml becomes the bootstrap language only \u2014 " + "needed once to get the self-hosting loop started, then never touched again")) + + (~docs/code :code (highlight ";; The bootstrap chain\n\nStep 0: Python evaluator (existing)\n \u2193 evaluates bootstrap_ml.py\nStep 1: OCaml evaluator (compiled from spec by Python)\n \u2193 evaluates compiler.sx\nStep 2: SX compiler (compiled from .sx by OCaml evaluator)\n \u2193 compiles itself\nStep 3: SX compiler (compiled by itself)\n \u2193 compiles everything\n \u2193 emits native, WASM, JS from .sx spec\n \u2193 OCaml is no longer in the chain" "text")) + + (p "At Step 3, the only language is SX. The compiler reads " (code ".sx") " and emits machine code. " + "OCaml was the scaffolding. The scaffolding comes down.")) + + + ;; ----------------------------------------------------------------------- + ;; Concurrent CEK on OCaml 5 + ;; ----------------------------------------------------------------------- + + (~docs/section :title "Concurrent CEK on OCaml 5" :id "concurrent-cek" + + (p "OCaml 5's concurrency model maps directly onto the " + (a :href "/sx/(etc.(plan.foundations))" "Foundations") " plan's concurrent CEK spec.") + + (h4 :class "font-semibold mt-4 mb-2" "Mapping") + (div :class "overflow-x-auto rounded border border-stone-200 mb-4" + (table :class "w-full text-left text-sm" + (thead (tr :class "border-b border-stone-200 bg-stone-100" + (th :class "px-3 py-2 font-medium text-stone-600" "SX primitive") + (th :class "px-3 py-2 font-medium text-stone-600" "OCaml 5") + (th :class "px-3 py-2 font-medium text-stone-600" "Characteristic"))) + (tbody + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" (code "spawn")) + (td :class "px-3 py-2 text-stone-700" "Fiber via " (code "perform Spawn")) + (td :class "px-3 py-2 text-stone-600" "Lightweight, scheduled by effect handler")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" (code "channel")) + (td :class "px-3 py-2 text-stone-700" (code "Eio.Stream")) + (td :class "px-3 py-2 text-stone-600" "Typed, bounded, backpressure")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" (code "yield!")) + (td :class "px-3 py-2 text-stone-700" (code "perform Yield")) + (td :class "px-3 py-2 text-stone-600" "Cooperative, zero-cost")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" (code "select")) + (td :class "px-3 py-2 text-stone-700" (code "Eio.Fiber.any")) + (td :class "px-3 py-2 text-stone-600" "First-to-complete")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" (code "fork-join")) + (td :class "px-3 py-2 text-stone-700" (code "Eio.Fiber.all")) + (td :class "px-3 py-2 text-stone-600" "Structured concurrency")) + (tr + (td :class "px-3 py-2 text-stone-700" "DAG scheduler") + (td :class "px-3 py-2 text-stone-700" "Domains + fiber pool") + (td :class "px-3 py-2 text-stone-600" "True parallelism across cores"))))) + + (p "Each concurrent CEK machine is a fiber. The scheduler is an effect handler. " + "This isn't simulating concurrency \u2014 it's using native concurrency whose mechanism " (em "is") " effects.") + + (h4 :class "font-semibold mt-4 mb-2" "The Art DAG connection") + (p "Art DAG's 3-phase execution (analyze \u2192 plan \u2192 execute) maps onto " + "concurrent CEK + OCaml domains:") + (ul :class "list-disc list-inside space-y-1 mt-2" + (li "Analyze: single CEK machine walks the DAG graph (one fiber)") + (li "Plan: resolve dependencies, topological sort (pure computation)") + (li "Execute: spawn one fiber per independent node, fan out to domains (true parallelism)") + (li "GPU kernels: fiber performs " (code "Gpu_dispatch") " effect, handler calls into GPU via C FFI"))) + + + ;; ----------------------------------------------------------------------- + ;; Linear effects + ;; ----------------------------------------------------------------------- + + (~docs/section :title "Linear Effects" :id "linear-effects" + + (p "The linearity axis from foundations. Two enforcement layers:") + + (h4 :class "font-semibold mt-4 mb-2" "Layer 1: SX type system (primary)") + (p "Quantity annotations in " (code "types.sx") " checked at spec-validation time:") + (ul :class "list-disc list-inside space-y-1 mt-2" + (li (code ":linear") " (1) \u2014 must be used exactly once") + (li (code ":affine") " (\u22641) \u2014 may be used at most once (can drop)") + (li (code ":unrestricted") " (\u03c9) \u2014 may be used any number of times")) + (p "Linear effects guarantee: a " (code "send-message") " effect is handled exactly once. " + "A channel is consumed. A resource handle is closed. " + "The type checker proves this before the evaluator ever runs.") + + (h4 :class "font-semibold mt-4 mb-2" "Layer 2: Host runtime (safety net)") + (p "OCaml 5's one-shot continuations enforce linearity at runtime. " + "A continuation can only be " (code "continue") "'d once \u2014 second invocation raises an exception. " + "This catches any bugs in the type checker itself.") + (p "If Koka replaces OCaml: compile-time enforcement replaces runtime enforcement. " + "The safety net becomes a proof. Same semantics, stronger guarantees.") + + (h4 :class "font-semibold mt-4 mb-2" "Decision point") + (p "When Step 5 (Linear Effects) of the foundations plan is reached:") + (ul :class "list-disc list-inside space-y-2 mt-2" + (li "If SX's type checker can enforce linearity reliably \u2192 " + (strong "stay on OCaml") ". Runtime one-shot is sufficient.") + (li "If linearity bugs keep slipping through \u2192 " + (strong "switch to Koka") ". Compile-time enforcement closes the gap.") + (li "If the self-hosting compiler (Phase 5) reaches maturity \u2192 " + (strong "it doesn't matter") ". SX compiles itself, the substrate is an implementation detail."))) + + + ;; ----------------------------------------------------------------------- + ;; Compiled all the way down + ;; ----------------------------------------------------------------------- + + (~docs/section :title "Compiled All the Way Down" :id "compiled" + + (p "The self-hosting compiler doesn't just compile the evaluator. " + "It compiles " (em "everything") ". Component definitions, page layouts, " + "event handlers, signal computations \u2014 all compiled to native machine code. " + "SX is not an interpreted scripting language with a nice spec. " + "It's a compiled language whose compiler also runs in the browser.") + + (h4 :class "font-semibold mt-4 mb-2" "JIT in the browser") + (p "The server sends SX (component definitions, page content). " + "The client receives it and " (strong "compiles to WASM and executes") ". " + "Not interprets. Not dispatches bytecodes. Compiles.") + + (~docs/code :code (highlight "Server sends: (defcomp ~card (&key title) (div :class \"card\" (h2 title)))\n\nClient does:\n 1. Parse SX source (fast \u2014 it's s-expressions)\n 2. Hash AST \u2192 CID\n 3. Cache hit? Call the already-compiled WASM function\n 4. Cache miss? Compile to WASM, cache by CID, call it\n\nStep 4 is the JIT. The compiler (itself WASM) emits a WASM\nfunction, instantiates it via WebAssembly.instantiate, caches\nthe module by CID. Next time: direct function call, zero overhead." "text")) + + (p "This is what V8 does with JavaScript. What LuaJIT does with Lua. " + "The difference: SX's semantics are simpler (no prototype chains, no " (code "this") + " binding, no implicit coercion), so the compiler is simpler. " + "And content-addressing means compiled artifacts are cacheable by CID \u2014 " + "compile once, store forever.") + + (h4 :class "font-semibold mt-4 mb-2" "The compilation tiers") + + (~docs/code :code (highlight "Tier 0: .sx source \u2192 tree-walking CEK (correct, slow \u2014 current)\nTier 1: .sx source \u2192 bytecodes \u2192 dispatch loop (correct, fast)\nTier 2: .sx source \u2192 WASM functions \u2192 execute (correct, fastest)\nTier 3: .sx source \u2192 native machine code (ahead-of-time, maximum)" "text")) + + (p "Each tier is faster. Tier 1 (bytecodes) is the " + (a :href "/sx/(etc.(plan.wasm-bytecode-vm))" "WASM Bytecode VM") + " plan \u2014 compact wire format, no parse overhead, better cache locality. " + "Tier 2 is JIT \u2014 the compiler emitting WASM functions on the fly. " + "Tier 3 is AOT \u2014 the entire app precompiled. " + "All tiers use the same spec, same platform layer, same platform primitives.") + + (h4 :class "font-semibold mt-4 mb-2" "Server-side precompilation") + (p "The server can compile too. Instead of sending SX source for the client to JIT, " + "send precompiled WASM:") + + (~docs/code :code (highlight ";; Option A: send SX source, client JIT compiles\nContent-Type: text/sx\n\n(div :class \"card\" (h2 \"hello\"))\n\n;; Option B: send precompiled WASM, client instantiates directly\nContent-Type: application/wasm\nX-Sx-Cid: bafyrei...\n\n" "text")) + + (p "Option B skips parsing and compilation entirely. The client instantiates " + "the WASM module and calls it. The server did all the work.") + + (h4 :class "font-semibold mt-4 mb-2" "Content-addressed compilation cache") + (p "Every " (code ".sx") " expression has a CID. Every compiled artifact has a CID. " + "The mapping is deterministic \u2014 the compiler is a pure function:") + + (~docs/code :code (highlight "source CID \u2192 compiled WASM CID\nbafyrei... \u2192 bafyrei...\n\nThis mapping is cacheable everywhere:\n\u2022 Browser cache \u2014 first visitor compiles, second visitor gets cached WASM\n\u2022 CDN \u2014 compiled artifacts served at the edge\n\u2022 IPFS \u2014 content-addressed by definition, globally deduplicated\n\u2022 Local disk \u2014 offline apps work from cached compiled components" "text")) + + (h4 :class "font-semibold mt-4 mb-2" "Entire apps as machine code") + (p "The entire application can be ahead-of-time compiled to a WASM binary. " + "Component definitions, page layouts, event handlers, signal computations \u2014 " + "all compiled to native WASM functions. The \"app\" is a " (code ".wasm") " file. " + "The platform layer provides DOM and fetch. Everything in between is compiled machine code.") + (p "The only thing that stays JIT-compiled is truly dynamic content \u2014 " + "user-generated SX, REPL input, " (code "eval") "'d strings. " + "And even those get JIT'd on first use and cached by CID.") + + (h4 :class "font-semibold mt-4 mb-2" "The architecture") + + (~docs/code :code (highlight "sx-platform.js \u2190 DOM, fetch, timers (the real world)\n \u2191 calls\nsx-compiler.wasm \u2190 the SX compiler (itself compiled to WASM)\n \u2191 compiles\n.sx source \u2190 received from server / cache / inline\n \u2193 emits\nnative WASM functions \u2190 cached by CID, instantiated on demand\n \u2193 executes\nactual DOM mutations via platform primitives" "text")) + + (p "The compiler is WASM. The code it produces is WASM. " + "It's compiled code all the way down. " + "The only interpreter in the system is the CPU.")) + + + ;; ----------------------------------------------------------------------- + ;; How this changes existing plans + ;; ----------------------------------------------------------------------- + + (~docs/section :title "Impact on Existing Plans" :id "impact" + + (div :class "overflow-x-auto rounded border border-stone-200 mb-4" + (table :class "w-full text-left text-sm" + (thead (tr :class "border-b border-stone-200 bg-stone-100" + (th :class "px-3 py-2 font-medium text-stone-600" "Plan") + (th :class "px-3 py-2 font-medium text-stone-600" "Impact"))) + (tbody + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" + (a :href "/sx/(etc.(plan.rust-wasm-host))" "Rust/WASM Host")) + (td :class "px-3 py-2 text-stone-600" + "Superseded. OCaml/WASM replaces Rust/WASM. The handle table and platform layer design carry over.")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" + (a :href "/sx/(etc.(plan.isolated-evaluator))" "Isolated Evaluator")) + (td :class "px-3 py-2 text-stone-600" + "Architecture preserved. sx-platform.js and evaluator isolation apply to the OCaml evaluator too. The JS evaluator becomes a fallback.")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" + (a :href "/sx/(etc.(plan.foundations))" "Foundations")) + (td :class "px-3 py-2 text-stone-600" + "Accelerated. OCaml 5 has native effects/continuations/fibers. Steps 4 (Concurrent CEK) and 5 (Linear Effects) map directly.")) + (tr :class "border-b border-stone-100" + (td :class "px-3 py-2 text-stone-700" + (a :href "/sx/(etc.(plan.wasm-bytecode-vm))" "WASM Bytecode VM")) + (td :class "px-3 py-2 text-stone-600" + "Complementary. OCaml gives tree-walking CEK. Bytecode VM is a future optimization on top.")) + (tr + (td :class "px-3 py-2 text-stone-700" + (a :href "/sx/(etc.(plan.self-hosting-bootstrapper))" "Self-Hosting Bootstrapper")) + (td :class "px-3 py-2 text-stone-600" + "Converges. js.sx and py.sx are self-hosting emitters. The self-hosting SX compiler (Phase 5 here) is the logical endpoint.")))))) + + + ;; ----------------------------------------------------------------------- + ;; Principles + ;; ----------------------------------------------------------------------- + + (~docs/section :title "Principles" :id "principles" + (ul :class "list-disc list-inside space-y-2" + (li (strong "SX is the language.") " Not OCaml, not Rust, not Haskell. " + "Those are substrates. SX defines the semantics, SX checks the types, " + "SX will ultimately compile itself.") + (li (strong "OCaml is scaffolding.") " The closest existing language to CEK. " + "Used to bootstrap the self-hosting compiler. Comes down when the compiler is mature.") + (li (strong "The spec is the compiler's input.") " " (code "cek.sx") ", " (code "frames.sx") + ", " (code "primitives.sx") " \u2014 the same files that define the language " + "become the compiler's source. One truth, one artifact.") + (li (strong "Platforms provide effects, not evaluation.") " Python provides database/HTTP. " + "JavaScript provides DOM. GPU provides tensor ops. " + "The evaluator is always compiled SX, embedded via FFI or WASM.") + (li (strong "Linearity belongs in the spec.") " SX's type system checks it. " + "The host provides runtime backup. If the type system is sound, the runtime check never fires.") + (li (strong "One language, every target.") " Not \"one spec, multiple implementations.\" " + "One " (em "compiled") " evaluator, deployed as native/.wasm/.js depending on context. " + "The evaluator binary is the same code everywhere. Only the platform layer changes."))) + + + ;; ----------------------------------------------------------------------- + ;; Outcome + ;; ----------------------------------------------------------------------- + + (~docs/section :title "Outcome" :id "outcome" + (p "After completion:") + (ul :class "list-disc list-inside space-y-2 mt-2" + (li "SX core evaluator compiled from spec via OCaml \u2014 native + WASM from one codebase.") + (li "Python web framework calls native " (code "sx_core.so") " for rendering \u2014 " + "100\u20131000x faster than interpreted Python.") + (li "Browser loads " (code "sx_core.wasm") " \u2014 same evaluator, same spec, near-native speed.") + (li "Concurrent CEK runs on OCaml 5 fibers/domains \u2014 true parallelism for Art DAG.") + (li "Linear effects validated by SX type system, enforced by OCaml one-shot continuations.") + (li "Self-hosting compiler: SX compiles itself to machine code. OCaml scaffolding removed.") + (li "The architecture proof: one language, one compiled evaluator, " + "every platform just provides effects.")) + + (p :class "text-stone-500 text-sm italic mt-12" + "The Mother Language was always SX. We just needed to find the right scaffolding to stand it up.")))) diff --git a/sx/sxc/pages/docs.sx b/sx/sxc/pages/docs.sx index c7240d5..e9b3a71 100644 --- a/sx/sxc/pages/docs.sx +++ b/sx/sxc/pages/docs.sx @@ -581,6 +581,8 @@ "cek-reactive" (~plans/cek-reactive/plan-cek-reactive-content) "reactive-runtime" (~plans/reactive-runtime/plan-reactive-runtime-content) "rust-wasm-host" (~plans/rust-wasm-host/plan-rust-wasm-host-content) + "isolated-evaluator" (~plans/isolated-evaluator/plan-isolated-evaluator-content) + "mother-language" (~plans/mother-language/plan-mother-language-content) :else (~plans/index/plans-index-content)))) ;; ---------------------------------------------------------------------------