Compare commits
4 Commits
9b4f735a0e
...
719da7914e
| Author | SHA1 | Date | |
|---|---|---|---|
| 719da7914e | |||
| c6a662c980 | |||
| e475222099 | |||
| b4df216fae |
@@ -112,16 +112,11 @@ def compile_ref_to_js(
|
||||
spec_mod_set.add("deps")
|
||||
if "page-helpers" in SPEC_MODULES:
|
||||
spec_mod_set.add("page-helpers")
|
||||
# CEK is the canonical evaluator — always included
|
||||
spec_mod_set.add("cek")
|
||||
spec_mod_set.add("frames")
|
||||
# cek module requires frames
|
||||
if "cek" in spec_mod_set:
|
||||
spec_mod_set.add("frames")
|
||||
# CEK is always included (part of evaluator.sx core file)
|
||||
has_cek = True
|
||||
has_deps = "deps" in spec_mod_set
|
||||
has_router = "router" in spec_mod_set
|
||||
has_page_helpers = "page-helpers" in spec_mod_set
|
||||
has_cek = "cek" in spec_mod_set
|
||||
|
||||
# Resolve extensions
|
||||
ext_set = set()
|
||||
@@ -132,9 +127,10 @@ def compile_ref_to_js(
|
||||
ext_set.add(e)
|
||||
has_continuations = "continuations" in ext_set
|
||||
|
||||
# Build file list: core + adapters + spec modules
|
||||
# Build file list: core evaluator + adapters + spec modules
|
||||
# evaluator.sx = merged frames + eval utilities + CEK machine
|
||||
sx_files = [
|
||||
("eval.sx", "eval"),
|
||||
("evaluator.sx", "evaluator (frames + eval + CEK)"),
|
||||
("render.sx", "render (core)"),
|
||||
]
|
||||
for name in ("parser", "html", "sx", "dom", "engine", "orchestration", "boot"):
|
||||
|
||||
@@ -46,14 +46,12 @@ SPEC_MODULES = {
|
||||
"router": ("router.sx", "router (client-side route matching)"),
|
||||
"signals": ("signals.sx", "signals (reactive signal runtime)"),
|
||||
"page-helpers": ("page-helpers.sx", "page-helpers (pure data transformation helpers)"),
|
||||
"frames": ("frames.sx", "frames (CEK continuation frames)"),
|
||||
"cek": ("cek.sx", "cek (explicit CEK machine evaluator)"),
|
||||
"types": ("types.sx", "types (gradual type system)"),
|
||||
}
|
||||
# Note: frames and cek are now part of evaluator.sx (always loaded as core)
|
||||
|
||||
# Explicit ordering for spec modules with dependencies.
|
||||
# Modules listed here are emitted in this order; any not listed use alphabetical.
|
||||
SPEC_MODULE_ORDER = ["deps", "frames", "page-helpers", "router", "cek", "signals", "types"]
|
||||
SPEC_MODULE_ORDER = ["deps", "page-helpers", "router", "signals", "types"]
|
||||
|
||||
|
||||
EXTENSION_NAMES = {"continuations"}
|
||||
|
||||
@@ -1484,15 +1484,14 @@ def compile_ref_to_py(
|
||||
spec_mod_set.add("page-helpers")
|
||||
if "router" in SPEC_MODULES:
|
||||
spec_mod_set.add("router")
|
||||
# CEK is the canonical evaluator — always include
|
||||
spec_mod_set.add("cek")
|
||||
spec_mod_set.add("frames")
|
||||
# CEK is always included (part of evaluator.sx core file)
|
||||
has_cek = True
|
||||
has_deps = "deps" in spec_mod_set
|
||||
has_cek = "cek" in spec_mod_set
|
||||
|
||||
# Core files always included, then selected adapters, then spec modules
|
||||
# evaluator.sx = merged frames + eval utilities + CEK machine
|
||||
sx_files = [
|
||||
("eval.sx", "eval"),
|
||||
("evaluator.sx", "evaluator (frames + eval + CEK)"),
|
||||
("forms.sx", "forms (server definition forms)"),
|
||||
("render.sx", "render (core)"),
|
||||
]
|
||||
|
||||
@@ -1636,14 +1636,12 @@ SPEC_MODULES = {
|
||||
"signals": ("signals.sx", "signals (reactive signal runtime)"),
|
||||
"page-helpers": ("page-helpers.sx", "page-helpers (pure data transformation helpers)"),
|
||||
"types": ("types.sx", "types (gradual type system)"),
|
||||
"frames": ("frames.sx", "frames (CEK continuation frames)"),
|
||||
"cek": ("cek.sx", "cek (explicit CEK machine evaluator)"),
|
||||
}
|
||||
# Note: frames and cek are now part of evaluator.sx (always loaded as core)
|
||||
|
||||
# Explicit ordering for spec modules with dependencies.
|
||||
# Modules listed here are emitted in this order; any not listed use alphabetical.
|
||||
SPEC_MODULE_ORDER = [
|
||||
"deps", "engine", "frames", "page-helpers", "router", "cek", "signals", "types",
|
||||
"deps", "engine", "page-helpers", "router", "signals", "types",
|
||||
]
|
||||
|
||||
EXTENSION_NAMES = {"continuations"}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -20,7 +20,7 @@
|
||||
:class "hidden md:flex md:flex-col max-w-xs md:h-full md:min-h-0 mr-3"
|
||||
(when aside aside))
|
||||
(section :id "main-panel"
|
||||
:class "flex-1 md:h-full md:min-h-0 md:overflow-y-auto md:overscroll-contain js-grid-viewport"
|
||||
:class "flex-1 md:h-full md:min-h-0 md:overflow-y-auto md:overscroll-contain overflow-x-hidden js-grid-viewport"
|
||||
(when content content)
|
||||
(div :class "pb-8")))))))
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
(div :id "root-menu" :sx-swap-oob "outerHTML" :class "md:hidden"
|
||||
(when menu menu))
|
||||
(section :id "main-panel"
|
||||
:class "flex-1 md:h-full md:min-h-0 md:overflow-y-auto md:overscroll-contain js-grid-viewport"
|
||||
:class "flex-1 md:h-full md:min-h-0 md:overflow-y-auto md:overscroll-contain overflow-x-hidden js-grid-viewport"
|
||||
(when content content))))
|
||||
|
||||
(defcomp ~shared:layout/hamburger ()
|
||||
|
||||
1188
spec/cek.sx
1188
spec/cek.sx
File diff suppressed because it is too large
Load Diff
@@ -1,248 +0,0 @@
|
||||
;; ==========================================================================
|
||||
;; continuations.sx — Delimited continuations (shift/reset)
|
||||
;;
|
||||
;; OPTIONAL EXTENSION — not required by the core evaluator.
|
||||
;; Bootstrappers include this only when the target requests it.
|
||||
;;
|
||||
;; Delimited continuations capture "the rest of the computation up to
|
||||
;; a delimiter." They are strictly less powerful than full call/cc but
|
||||
;; cover the practical use cases: suspendable rendering, cooperative
|
||||
;; scheduling, linear async flows, wizard forms, and undo.
|
||||
;;
|
||||
;; Two new special forms:
|
||||
;; (reset body) — establish a delimiter
|
||||
;; (shift k body) — capture the continuation to the nearest reset
|
||||
;;
|
||||
;; One new type:
|
||||
;; continuation — a captured delimited continuation, callable
|
||||
;;
|
||||
;; The captured continuation is a function of one argument. Invoking it
|
||||
;; provides the value that the shift expression "returns" within the
|
||||
;; delimited context, then completes the rest of the reset body.
|
||||
;;
|
||||
;; Continuations are composable — invoking a continuation returns a
|
||||
;; value (the result of the reset body), which can be used normally.
|
||||
;; This is the key difference from undelimited call/cc, where invoking
|
||||
;; a continuation never returns.
|
||||
;;
|
||||
;; Platform requirements:
|
||||
;; (make-continuation fn) — wrap a function as a continuation value
|
||||
;; (continuation? x) — type predicate
|
||||
;; (type-of continuation) → "continuation"
|
||||
;; Continuations are callable (same dispatch as lambda).
|
||||
;; ==========================================================================
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 1. Type
|
||||
;; --------------------------------------------------------------------------
|
||||
;;
|
||||
;; A continuation is a callable value of one argument.
|
||||
;;
|
||||
;; (continuation? k) → true if k is a captured continuation
|
||||
;; (type-of k) → "continuation"
|
||||
;; (k value) → invoke: resume the captured computation with value
|
||||
;;
|
||||
;; Continuations are first-class: they can be stored in variables, passed
|
||||
;; as arguments, returned from functions, and put in data structures.
|
||||
;;
|
||||
;; Invoking a delimited continuation RETURNS a value — the result of the
|
||||
;; reset body. This makes them composable:
|
||||
;;
|
||||
;; (+ 1 (reset (+ 10 (shift k (k 5)))))
|
||||
;; ;; k is "add 10 to _ and return from reset"
|
||||
;; ;; (k 5) → 15, which is returned from reset
|
||||
;; ;; (+ 1 15) → 16
|
||||
;;
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 2. reset — establish a continuation delimiter
|
||||
;; --------------------------------------------------------------------------
|
||||
;;
|
||||
;; (reset body)
|
||||
;;
|
||||
;; Evaluates body in the current environment. If no shift occurs during
|
||||
;; evaluation of body, reset simply returns the value of body.
|
||||
;;
|
||||
;; If shift occurs, reset is the boundary — the continuation captured by
|
||||
;; shift extends from the shift point back to (and including) this reset.
|
||||
;;
|
||||
;; reset is the "prompt" — it marks where the continuation stops.
|
||||
;;
|
||||
;; Semantics:
|
||||
;; (reset expr) where expr contains no shift
|
||||
;; → (eval expr env) ;; just evaluates normally
|
||||
;;
|
||||
;; (reset ... (shift k body) ...)
|
||||
;; → captures continuation, evaluates shift's body
|
||||
;; → the result of the shift body is the result of the reset
|
||||
;;
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define sf-reset
|
||||
(fn ((args :as list) (env :as dict))
|
||||
;; Single argument: the body expression.
|
||||
;; Install a continuation delimiter, then evaluate body.
|
||||
;; The implementation is target-specific:
|
||||
;; - In Scheme: native reset/shift
|
||||
;; - In Haskell: Control.Monad.CC or delimited continuations library
|
||||
;; - In Python: coroutine/generator-based (see implementation notes)
|
||||
;; - In JavaScript: generator-based or CPS transform
|
||||
;; - In Rust: CPS transform at compile time
|
||||
(let ((body (first args)))
|
||||
(eval-with-delimiter body env))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 3. shift — capture the continuation to the nearest reset
|
||||
;; --------------------------------------------------------------------------
|
||||
;;
|
||||
;; (shift k body)
|
||||
;;
|
||||
;; Captures the continuation from this point back to the nearest enclosing
|
||||
;; reset and binds it to k. Then evaluates body in the current environment
|
||||
;; extended with k. The result of body becomes the result of the enclosing
|
||||
;; reset.
|
||||
;;
|
||||
;; k is a function of one argument. Calling (k value) resumes the captured
|
||||
;; computation with value standing in for the shift expression.
|
||||
;;
|
||||
;; The continuation k is composable: (k value) returns a value (the result
|
||||
;; of the reset body when resumed with value). This means k can be called
|
||||
;; multiple times, and its result can be used in further computation.
|
||||
;;
|
||||
;; Examples:
|
||||
;;
|
||||
;; ;; Basic: shift provides a value to the surrounding computation
|
||||
;; (reset (+ 1 (shift k (k 41))))
|
||||
;; ;; k = "add 1 to _", (k 41) → 42, reset returns 42
|
||||
;;
|
||||
;; ;; Abort: shift can discard the continuation entirely
|
||||
;; (reset (+ 1 (shift k "aborted")))
|
||||
;; ;; k is never called, reset returns "aborted"
|
||||
;;
|
||||
;; ;; Multiple invocations: k can be called more than once
|
||||
;; (reset (+ 1 (shift k (list (k 10) (k 20)))))
|
||||
;; ;; (k 10) → 11, (k 20) → 21, reset returns (11 21)
|
||||
;;
|
||||
;; ;; Stored for later: k can be saved and invoked outside reset
|
||||
;; (define saved nil)
|
||||
;; (reset (+ 1 (shift k (set! saved k) 0)))
|
||||
;; ;; reset returns 0, saved holds the continuation
|
||||
;; (saved 99) ;; → 100
|
||||
;;
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define sf-shift
|
||||
(fn ((args :as list) (env :as dict))
|
||||
;; Two arguments: the continuation variable name, and the body.
|
||||
(let ((k-name (symbol-name (first args)))
|
||||
(body (second args)))
|
||||
;; Capture the current continuation up to the nearest reset.
|
||||
;; Bind it to k-name in the environment, then evaluate body.
|
||||
;; The result of body is returned to the reset.
|
||||
(capture-continuation k-name body env))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 4. Interaction with other features
|
||||
;; --------------------------------------------------------------------------
|
||||
;;
|
||||
;; TCO (trampoline):
|
||||
;; Continuations interact naturally with the trampoline. A shift inside
|
||||
;; a tail-call position captures the continuation including the pending
|
||||
;; return. The trampoline resolves thunks before the continuation is
|
||||
;; delimited.
|
||||
;;
|
||||
;; Macros:
|
||||
;; shift/reset are special forms, not macros. Macros expand before
|
||||
;; evaluation, so shift inside a macro-expanded form works correctly —
|
||||
;; it captures the continuation of the expanded code.
|
||||
;;
|
||||
;; Components:
|
||||
;; shift inside a component body captures the continuation of that
|
||||
;; component's render. The enclosing reset determines the delimiter.
|
||||
;; This is the foundation for suspendable rendering — a component can
|
||||
;; shift to suspend, and the server resumes it when data arrives.
|
||||
;;
|
||||
;; I/O primitives:
|
||||
;; I/O primitives execute at invocation time, in whatever context
|
||||
;; exists then. A continuation that captures a computation containing
|
||||
;; I/O will re-execute that I/O when invoked. If the I/O requires
|
||||
;; request context (e.g. current-user), invoking the continuation
|
||||
;; outside a request will fail — same as calling the I/O directly.
|
||||
;; This is consistent, not a restriction.
|
||||
;;
|
||||
;; In typed targets (Haskell, Rust), the type system can enforce that
|
||||
;; continuations containing I/O are only invoked in appropriate contexts.
|
||||
;; In dynamic targets (Python, JS), it fails at runtime.
|
||||
;;
|
||||
;; Lexical scope:
|
||||
;; Continuations capture the dynamic extent (what happens next) but
|
||||
;; close over the lexical environment at the point of capture. Variable
|
||||
;; bindings in the continuation refer to the same environment — mutations
|
||||
;; via set! are visible.
|
||||
;;
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 5. Implementation notes per target
|
||||
;; --------------------------------------------------------------------------
|
||||
;;
|
||||
;; The bootstrapper emits target-specific continuation machinery.
|
||||
;; The spec defines semantics; each target chooses representation.
|
||||
;;
|
||||
;; Scheme / Racket:
|
||||
;; Native shift/reset. No transformation needed. The bootstrapper
|
||||
;; emits (require racket/control) or equivalent.
|
||||
;;
|
||||
;; Haskell:
|
||||
;; Control.Monad.CC provides delimited continuations in the CC monad.
|
||||
;; Alternatively, the evaluator can be CPS-transformed at compile time.
|
||||
;; Continuations become first-class functions naturally.
|
||||
;;
|
||||
;; Python:
|
||||
;; Generator-based: reset creates a generator, shift yields from it.
|
||||
;; The trampoline loop drives the generator. Each yield is a shift
|
||||
;; point, and send() provides the resume value.
|
||||
;; Alternative: greenlet-based (stackful coroutines).
|
||||
;;
|
||||
;; JavaScript:
|
||||
;; Generator-based (function* / yield). Similar to Python.
|
||||
;; Alternative: CPS transform at bootstrap time — the bootstrapper
|
||||
;; rewrites the evaluator into continuation-passing style, making
|
||||
;; shift/reset explicit function arguments.
|
||||
;;
|
||||
;; Rust:
|
||||
;; CPS transform at compile time. Continuations become enum variants
|
||||
;; or boxed closures. The type system ensures continuations are used
|
||||
;; linearly if desired (affine types via ownership).
|
||||
;;
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 6. Platform interface — what each target must provide
|
||||
;; --------------------------------------------------------------------------
|
||||
;;
|
||||
;; (eval-with-delimiter expr env)
|
||||
;; Install a reset delimiter, evaluate expr, return result.
|
||||
;; If expr calls shift, the continuation is captured up to here.
|
||||
;;
|
||||
;; (capture-continuation k-name body env)
|
||||
;; Capture the current continuation up to the nearest delimiter.
|
||||
;; Bind it to k-name in env, evaluate body, return result to delimiter.
|
||||
;;
|
||||
;; (make-continuation fn)
|
||||
;; Wrap a native function as a continuation value.
|
||||
;;
|
||||
;; (continuation? x)
|
||||
;; Type predicate.
|
||||
;;
|
||||
;; Continuations must be callable via the standard function-call
|
||||
;; dispatch in eval-list (same path as lambda calls).
|
||||
;;
|
||||
;; --------------------------------------------------------------------------
|
||||
1272
spec/eval.sx
1272
spec/eval.sx
File diff suppressed because it is too large
Load Diff
2335
spec/evaluator.sx
Normal file
2335
spec/evaluator.sx
Normal file
File diff suppressed because it is too large
Load Diff
262
spec/frames.sx
262
spec/frames.sx
@@ -1,262 +0,0 @@
|
||||
;; ==========================================================================
|
||||
;; frames.sx — CEK machine frame types
|
||||
;;
|
||||
;; Defines the continuation frame types used by the explicit CEK evaluator.
|
||||
;; Each frame represents a "what to do next" when a sub-evaluation completes.
|
||||
;;
|
||||
;; A CEK state is a dict:
|
||||
;; {:control expr — expression being evaluated (or nil in continue phase)
|
||||
;; :env env — current environment
|
||||
;; :kont list — continuation: list of frames (stack, head = top)
|
||||
;; :phase "eval"|"continue"
|
||||
;; :value any} — value produced (only in continue phase)
|
||||
;;
|
||||
;; Two-phase step function:
|
||||
;; step-eval: control is expression → dispatch → push frame + new control
|
||||
;; step-continue: value produced → pop frame → dispatch → new state
|
||||
;;
|
||||
;; Terminal state: phase = "continue" and kont is empty → value is final result.
|
||||
;; ==========================================================================
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 1. CEK State constructors
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define make-cek-state
|
||||
(fn (control env kont)
|
||||
{:control control :env env :kont kont :phase "eval" :value nil}))
|
||||
|
||||
(define make-cek-value
|
||||
(fn (value env kont)
|
||||
{:control nil :env env :kont kont :phase "continue" :value value}))
|
||||
|
||||
(define cek-terminal?
|
||||
(fn (state)
|
||||
(and (= (get state "phase") "continue")
|
||||
(empty? (get state "kont")))))
|
||||
|
||||
(define cek-control (fn (s) (get s "control")))
|
||||
(define cek-env (fn (s) (get s "env")))
|
||||
(define cek-kont (fn (s) (get s "kont")))
|
||||
(define cek-phase (fn (s) (get s "phase")))
|
||||
(define cek-value (fn (s) (get s "value")))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 2. Frame constructors
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Each frame type is a dict with a "type" key and frame-specific data.
|
||||
|
||||
;; IfFrame: waiting for condition value
|
||||
;; After condition evaluates, choose then or else branch
|
||||
(define make-if-frame
|
||||
(fn (then-expr else-expr env)
|
||||
{:type "if" :then then-expr :else else-expr :env env}))
|
||||
|
||||
;; WhenFrame: waiting for condition value
|
||||
;; If truthy, evaluate body exprs sequentially
|
||||
(define make-when-frame
|
||||
(fn (body-exprs env)
|
||||
{:type "when" :body body-exprs :env env}))
|
||||
|
||||
;; BeginFrame: sequential evaluation
|
||||
;; Remaining expressions to evaluate after current one
|
||||
(define make-begin-frame
|
||||
(fn (remaining env)
|
||||
{:type "begin" :remaining remaining :env env}))
|
||||
|
||||
;; LetFrame: binding evaluation in progress
|
||||
;; name = current binding name, remaining = remaining (name val) pairs
|
||||
;; body = body expressions to evaluate after all bindings
|
||||
(define make-let-frame
|
||||
(fn (name remaining body local)
|
||||
{:type "let" :name name :remaining remaining :body body :env local}))
|
||||
|
||||
;; DefineFrame: waiting for value to bind
|
||||
(define make-define-frame
|
||||
(fn (name env has-effects effect-list)
|
||||
{:type "define" :name name :env env
|
||||
:has-effects has-effects :effect-list effect-list}))
|
||||
|
||||
;; SetFrame: waiting for value to assign
|
||||
(define make-set-frame
|
||||
(fn (name env)
|
||||
{:type "set" :name name :env env}))
|
||||
|
||||
;; ArgFrame: evaluating function arguments
|
||||
;; f = function value (already evaluated), evaled = already evaluated args
|
||||
;; remaining = remaining arg expressions
|
||||
(define make-arg-frame
|
||||
(fn (f evaled remaining env raw-args head-name)
|
||||
{:type "arg" :f f :evaled evaled :remaining remaining :env env
|
||||
:raw-args raw-args :head-name (or head-name nil)}))
|
||||
|
||||
;; CallFrame: about to call with fully evaluated args
|
||||
(define make-call-frame
|
||||
(fn (f args env)
|
||||
{:type "call" :f f :args args :env env}))
|
||||
|
||||
;; CondFrame: evaluating cond clauses
|
||||
(define make-cond-frame
|
||||
(fn (remaining env scheme?)
|
||||
{:type "cond" :remaining remaining :env env :scheme scheme?}))
|
||||
|
||||
;; CaseFrame: evaluating case clauses
|
||||
(define make-case-frame
|
||||
(fn (match-val remaining env)
|
||||
{:type "case" :match-val match-val :remaining remaining :env env}))
|
||||
|
||||
;; ThreadFirstFrame: pipe threading
|
||||
(define make-thread-frame
|
||||
(fn (remaining env)
|
||||
{:type "thread" :remaining remaining :env env}))
|
||||
|
||||
;; MapFrame: higher-order map/map-indexed in progress
|
||||
(define make-map-frame
|
||||
(fn (f remaining results env)
|
||||
{:type "map" :f f :remaining remaining :results results :env env :indexed false}))
|
||||
|
||||
(define make-map-indexed-frame
|
||||
(fn (f remaining results env)
|
||||
{:type "map" :f f :remaining remaining :results results :env env :indexed true}))
|
||||
|
||||
;; FilterFrame: higher-order filter in progress
|
||||
(define make-filter-frame
|
||||
(fn (f remaining results current-item env)
|
||||
{:type "filter" :f f :remaining remaining :results results
|
||||
:current-item current-item :env env}))
|
||||
|
||||
;; ReduceFrame: higher-order reduce in progress
|
||||
(define make-reduce-frame
|
||||
(fn (f remaining env)
|
||||
{:type "reduce" :f f :remaining remaining :env env}))
|
||||
|
||||
;; ForEachFrame: higher-order for-each in progress
|
||||
(define make-for-each-frame
|
||||
(fn (f remaining env)
|
||||
{:type "for-each" :f f :remaining remaining :env env}))
|
||||
|
||||
;; SomeFrame: higher-order some (short-circuit on first truthy)
|
||||
(define make-some-frame
|
||||
(fn (f remaining env)
|
||||
{:type "some" :f f :remaining remaining :env env}))
|
||||
|
||||
;; EveryFrame: higher-order every? (short-circuit on first falsy)
|
||||
(define make-every-frame
|
||||
(fn (f remaining env)
|
||||
{:type "every" :f f :remaining remaining :env env}))
|
||||
|
||||
;; ScopeFrame: scope-pop! when frame pops
|
||||
(define make-scope-frame
|
||||
(fn (name remaining env)
|
||||
{:type "scope" :name name :remaining remaining :env env}))
|
||||
|
||||
;; ResetFrame: delimiter for shift/reset continuations
|
||||
(define make-reset-frame
|
||||
(fn (env)
|
||||
{:type "reset" :env env}))
|
||||
|
||||
;; DictFrame: evaluating dict values
|
||||
(define make-dict-frame
|
||||
(fn (remaining results env)
|
||||
{:type "dict" :remaining remaining :results results :env env}))
|
||||
|
||||
;; AndFrame: short-circuit and
|
||||
(define make-and-frame
|
||||
(fn (remaining env)
|
||||
{:type "and" :remaining remaining :env env}))
|
||||
|
||||
;; OrFrame: short-circuit or
|
||||
(define make-or-frame
|
||||
(fn (remaining env)
|
||||
{:type "or" :remaining remaining :env env}))
|
||||
|
||||
;; QuasiquoteFrame (not a real frame — QQ is handled specially)
|
||||
|
||||
;; DynamicWindFrame: phases of dynamic-wind
|
||||
(define make-dynamic-wind-frame
|
||||
(fn (phase body-thunk after-thunk env)
|
||||
{:type "dynamic-wind" :phase phase
|
||||
:body-thunk body-thunk :after-thunk after-thunk :env env}))
|
||||
|
||||
;; ReactiveResetFrame: delimiter for reactive deref-as-shift
|
||||
;; Carries an update-fn that gets called with new values on re-render.
|
||||
(define make-reactive-reset-frame
|
||||
(fn (env update-fn first-render?)
|
||||
{:type "reactive-reset" :env env :update-fn update-fn
|
||||
:first-render first-render?}))
|
||||
|
||||
;; DerefFrame: awaiting evaluation of deref's argument
|
||||
(define make-deref-frame
|
||||
(fn (env)
|
||||
{:type "deref" :env env}))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 3. Frame accessors
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define frame-type (fn (f) (get f "type")))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 4. Continuation operations
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define kont-push
|
||||
(fn (frame kont) (cons frame kont)))
|
||||
|
||||
(define kont-top
|
||||
(fn (kont) (first kont)))
|
||||
|
||||
(define kont-pop
|
||||
(fn (kont) (rest kont)))
|
||||
|
||||
(define kont-empty?
|
||||
(fn (kont) (empty? kont)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 5. CEK shift/reset support
|
||||
;; --------------------------------------------------------------------------
|
||||
;; shift captures all frames up to the nearest ResetFrame.
|
||||
;; reset pushes a ResetFrame.
|
||||
|
||||
(define kont-capture-to-reset
|
||||
(fn (kont)
|
||||
;; Returns (captured-frames remaining-kont).
|
||||
;; captured-frames: frames from top up to (not including) ResetFrame.
|
||||
;; remaining-kont: frames after ResetFrame.
|
||||
;; Stops at either "reset" or "reactive-reset" frames.
|
||||
(define scan
|
||||
(fn (k captured)
|
||||
(if (empty? k)
|
||||
(error "shift without enclosing reset")
|
||||
(let ((frame (first k)))
|
||||
(if (or (= (frame-type frame) "reset")
|
||||
(= (frame-type frame) "reactive-reset"))
|
||||
(list captured (rest k))
|
||||
(scan (rest k) (append captured (list frame))))))))
|
||||
(scan kont (list))))
|
||||
|
||||
;; Check if a ReactiveResetFrame exists anywhere in the continuation
|
||||
(define has-reactive-reset-frame?
|
||||
(fn (kont)
|
||||
(if (empty? kont) false
|
||||
(if (= (frame-type (first kont)) "reactive-reset") true
|
||||
(has-reactive-reset-frame? (rest kont))))))
|
||||
|
||||
;; Capture frames up to nearest ReactiveResetFrame.
|
||||
;; Returns (captured-frames, reset-frame, remaining-kont).
|
||||
(define kont-capture-to-reactive-reset
|
||||
(fn (kont)
|
||||
(define scan
|
||||
(fn (k captured)
|
||||
(if (empty? k)
|
||||
(error "reactive deref without enclosing reactive-reset")
|
||||
(let ((frame (first k)))
|
||||
(if (= (frame-type frame) "reactive-reset")
|
||||
(list captured frame (rest k))
|
||||
(scan (rest k) (append captured (list frame))))))))
|
||||
(scan kont (list))))
|
||||
@@ -70,20 +70,20 @@
|
||||
(next-idx (mod (+ idx 1) count))
|
||||
(prev-node (nth sibs prev-idx))
|
||||
(next-node (nth sibs next-idx)))
|
||||
(div :class "max-w-3xl mx-auto px-4 py-2 grid grid-cols-3 items-center"
|
||||
(div :class "w-full max-w-3xl mx-auto px-4 py-2 grid grid-cols-3 items-center"
|
||||
:style (str "opacity:" row-opacity ";transition:opacity 0.3s;")
|
||||
(a :href (get prev-node "href")
|
||||
:sx-get (get prev-node "href") :sx-target "#main-panel"
|
||||
:sx-select "#main-panel" :sx-swap "outerHTML"
|
||||
:sx-push-url "true"
|
||||
:class "text-right"
|
||||
:class "text-right min-w-0 truncate"
|
||||
:style (tw "text-stone-500 text-sm")
|
||||
(str "← " (get prev-node "label")))
|
||||
(str "\u2190 " (get prev-node "label")))
|
||||
(a :href (get node "href")
|
||||
:sx-get (get node "href") :sx-target "#main-panel"
|
||||
:sx-select "#main-panel" :sx-swap "outerHTML"
|
||||
:sx-push-url "true"
|
||||
:class "text-center px-4"
|
||||
:class "text-center min-w-0 truncate px-1"
|
||||
:style (if is-leaf
|
||||
(tw "text-violet-700 text-2xl font-bold")
|
||||
(tw "text-violet-700 text-lg font-semibold"))
|
||||
@@ -92,9 +92,9 @@
|
||||
:sx-get (get next-node "href") :sx-target "#main-panel"
|
||||
:sx-select "#main-panel" :sx-swap "outerHTML"
|
||||
:sx-push-url "true"
|
||||
:class "text-left"
|
||||
:class "text-left min-w-0 truncate"
|
||||
:style (tw "text-stone-500 text-sm")
|
||||
(str (get next-node "label") " →")))))))
|
||||
(str (get next-node "label") " \u2192")))))))
|
||||
|
||||
;; Children links — shown as clearly clickable buttons.
|
||||
(defcomp ~layouts/nav-children (&key items)
|
||||
|
||||
@@ -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))"
|
||||
|
||||
578
sx/sx/plans/mother-language.sx
Normal file
578
sx/sx/plans/mother-language.sx
Normal file
@@ -0,0 +1,578 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; 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<Vec<Value>>")
|
||||
" 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<binary WASM module>" "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."))
|
||||
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
;; Security model
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~docs/section :title "Security Model" :id "security"
|
||||
|
||||
(p "Compiled SX running as WASM is " (em "more secure") " than plain JavaScript, "
|
||||
"not less. JS has ambient access to the full browser API. "
|
||||
"WASM + the platform layer means compiled SX code has "
|
||||
(strong "zero ambient capabilities") " \u2014 every capability is explicitly granted.")
|
||||
|
||||
(h4 :class "font-semibold mt-4 mb-2" "Five defence layers")
|
||||
|
||||
(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" "Layer")
|
||||
(th :class "px-3 py-2 font-medium text-stone-600" "Enforced by")
|
||||
(th :class "px-3 py-2 font-medium text-stone-600" "What it prevents")))
|
||||
(tbody
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "1. WASM sandbox")
|
||||
(td :class "px-3 py-2 text-stone-700" "Browser")
|
||||
(td :class "px-3 py-2 text-stone-600" "Memory isolation, no system calls, no DOM access except via explicit imports. Validated before execution."))
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "2. Platform capabilities")
|
||||
(td :class "px-3 py-2 text-stone-700" (code "sx-platform.js"))
|
||||
(td :class "px-3 py-2 text-stone-600" "Compiled code can only call functions you register. No fetch? Can't fetch. No localStorage? Can't read storage. The platform is a capability system."))
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "3. Content-addressed verification")
|
||||
(td :class "px-3 py-2 text-stone-700" "CID determinism")
|
||||
(td :class "px-3 py-2 text-stone-600" "Compiler is deterministic: same source \u2192 same CID. Client can re-compile and verify. Tampered WASM produces wrong CID \u2192 reject."))
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "4. Per-component attenuation")
|
||||
(td :class "px-3 py-2 text-stone-700" "Platform scoping")
|
||||
(td :class "px-3 py-2 text-stone-600" "Different components get different capability subsets. User-generated content gets a locked-down platform \u2014 can render DOM but can't fetch or listen to events."))
|
||||
(tr
|
||||
(td :class "px-3 py-2 text-stone-700" "5. Source-first fallback")
|
||||
(td :class "px-3 py-2 text-stone-700" "Client compiler")
|
||||
(td :class "px-3 py-2 text-stone-600" "Don't trust precompiled WASM? Compile from source locally. The client has the compiler. Precompilation is an optimisation, not a trust requirement.")))))
|
||||
|
||||
(h4 :class "font-semibold mt-4 mb-2" "Content-addressed tamper detection")
|
||||
(p "The server sends both SX source and precompiled WASM CID. The client can verify:")
|
||||
|
||||
(~docs/code :code (highlight ";; Server sends:\nContent-Type: application/wasm\nX-Sx-Source-Cid: bafyrei..source\nX-Sx-Compiled-Cid: bafyrei..compiled\n\n;; Client verifies (optional, configurable):\n1. Hash the WASM binary \u2192 matches X-Sx-Compiled-Cid?\n2. Compile source locally \u2192 produces same compiled CID?\n3. Check manifest of pinned CIDs \u2192 CID is expected?\n\n;; Any mismatch = tampered = reject" "text"))
|
||||
|
||||
(h4 :class "font-semibold mt-4 mb-2" "Capability attenuation per component")
|
||||
(p "The platform scopes capabilities per evaluator instance. "
|
||||
"App shell gets full access. Third-party or user-generated content gets the minimum:")
|
||||
|
||||
(~docs/code :code (highlight "// Full capabilities for the app shell\nplatform.registerAll(appShellCompiler);\n\n// Restricted for user-generated content\nplatform.registerSubset(userContentCompiler, {\n allow: [\"dom-create-element\", \"dom-set-attr\", \"dom-append\",\n \"dom-create-text-node\", \"dom-set-text\"],\n deny: [\"fetch\", \"localStorage\", \"dom-listen\",\n \"dom-set-inner-html\", \"eval\"]\n});\n\n// The restricted compiler's WASM module literally doesn't\n// have imports for the denied functions. Not just blocked\n// at runtime \u2014 absent from the binary." "javascript"))
|
||||
|
||||
(h4 :class "font-semibold mt-4 mb-2" "Component manifests")
|
||||
(p "The app ships with a manifest of expected CIDs for its core components. "
|
||||
"Like subresource integrity (SRI) but for compiled code:")
|
||||
|
||||
(~docs/code :code (highlight ";; Component manifest (shipped with the app, signed)\n{\n \"~card\": \"bafyrei..abc\"\n \"~header\": \"bafyrei..def\"\n \"~nav-item\": \"bafyrei..ghi\"\n}\n\n;; On navigation: server sends component update\n;; Client compiles \u2192 checks CID against manifest\n;; Match = trusted, execute\n;; Mismatch = tampered, reject and report" "text"))
|
||||
|
||||
(p "The security model is " (em "structural") ", not bolt-on. "
|
||||
"WASM isolation, platform capabilities, content-addressed verification, "
|
||||
"and per-component attenuation all arise naturally from the architecture. "
|
||||
"The platform layer that enables Rust/OCaml interop is the same layer "
|
||||
"that enforces security boundaries."))
|
||||
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
;; Isomorphic rendering + SEO
|
||||
;; -----------------------------------------------------------------------
|
||||
|
||||
(~docs/section :title "Isomorphic Rendering" :id "isomorphic"
|
||||
|
||||
(p "WASM is invisible to search engines. But SX is already isomorphic \u2014 "
|
||||
"the same spec, the same components, rendered to HTML on the server "
|
||||
"and to DOM on the client. Compiled WASM doesn't change this. "
|
||||
"It makes the client side faster without affecting what crawlers see.")
|
||||
|
||||
(h4 :class "font-semibold mt-4 mb-2" "The rendering pipeline")
|
||||
|
||||
(~docs/code :code (highlight "Crawler visits:\n GET /page\n \u2192 Server compiles SX (native OCaml)\n \u2192 render-to-html (adapter-html.sx)\n \u2192 Full static HTML with semantic markup\n \u2192 Google indexes it\n\nUser first visit:\n GET /page\n \u2192 Server renders HTML (same as crawler)\n \u2192 Browser displays immediately (no JS needed)\n \u2192 Client loads sx-compiler.wasm + sx-platform.js\n \u2192 Hydrates: attaches event handlers, activates islands\n \u2192 Page is interactive\n\nUser navigates (SPA):\n sx-get /next-page\n \u2192 Server sends SX wire format (aser)\n \u2192 Client compiles + renders via WASM\n \u2192 Morph engine patches the DOM" "text"))
|
||||
|
||||
(p "The server and client have the " (em "same compiler") " from the " (em "same spec") ". "
|
||||
(code "adapter-html.sx") " produces HTML strings. "
|
||||
(code "adapter-dom.sx") " produces DOM nodes. "
|
||||
"Two rendering modes of one evaluator. The compiled WASM version "
|
||||
"makes hydration and SPA navigation faster, but the initial HTML "
|
||||
"is always server-rendered.")
|
||||
|
||||
(h4 :class "font-semibold mt-4 mb-2" "What crawlers see")
|
||||
(ul :class "list-disc list-inside space-y-1 mt-2"
|
||||
(li "Fully rendered HTML \u2014 no \"loading...\" skeleton, no JS-dependent content")
|
||||
(li "Semantic markup \u2014 " (code "<h1>") ", " (code "<nav>") ", " (code "<article>") ", " (code "<a href>")
|
||||
" \u2014 all from the SX component tree")
|
||||
(li "Meta tags, canonical URLs, structured data \u2014 rendered server-side by " (code "shell.sx"))
|
||||
(li "No WASM, no JS required \u2014 the HTML is the page, complete on first byte"))
|
||||
|
||||
(h4 :class "font-semibold mt-4 mb-2" "Content-addressed prerendering")
|
||||
(p "The server can prerender every page to static HTML, hash it, "
|
||||
"and cache it at the CDN edge:")
|
||||
|
||||
(~docs/code :code (highlight "Page source CID \u2192 Rendered HTML CID\nbafyrei..source \u2192 bafyrei..html\n\n\u2022 Crawler hits CDN \u2192 instant HTML, no server round-trip\n\u2022 Page content changes \u2192 new source CID \u2192 new HTML CID \u2192 CDN invalidated\n\u2022 Same CID = same HTML forever \u2192 infinite cache, zero revalidation" "text"))
|
||||
|
||||
(p "This is the same content-addressed caching as compiled WASM, "
|
||||
"applied to the HTML output. Both the compiled client code and "
|
||||
"the server-rendered HTML are cached by CID. "
|
||||
"The entire delivery pipeline is content-addressed.")
|
||||
|
||||
(h4 :class "font-semibold mt-4 mb-2" "Progressive enhancement")
|
||||
(p "The page works at every level:")
|
||||
(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" "Client capability")
|
||||
(th :class "px-3 py-2 font-medium text-stone-600" "Experience")))
|
||||
(tbody
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "No JS (crawler, reader mode)")
|
||||
(td :class "px-3 py-2 text-stone-600" "Full HTML. Links work. Forms submit. Content is complete."))
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "JS, no WASM")
|
||||
(td :class "px-3 py-2 text-stone-600" "Falls back to js_of_ocaml evaluator or interpreted JS. SPA navigation, islands, signals all work."))
|
||||
(tr
|
||||
(td :class "px-3 py-2 text-stone-700" "JS + WASM")
|
||||
(td :class "px-3 py-2 text-stone-600" "Full compiled pipeline. JIT compilation, cached WASM functions, near-native rendering speed."))))))
|
||||
|
||||
|
||||
;; -----------------------------------------------------------------------
|
||||
;; 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"
|
||||
"Subsumed as Tier 1. Bytecodes become an intermediate step on the path to native WASM compilation. The dispatch loop is Tier 1; JIT to WASM functions is Tier 2; AOT is Tier 3."))
|
||||
(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 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 compiler, same spec, near-native speed.")
|
||||
(li "Server sends SX, client JIT-compiles to WASM functions and caches by CID.")
|
||||
(li "Entire apps AOT-compiled to " (code ".wasm") " binaries. "
|
||||
"Platform provides DOM/fetch. Everything else is machine code.")
|
||||
(li "Content-addressed compilation cache: compile once, cache by CID, serve from CDN/IPFS forever.")
|
||||
(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 only interpreter in the system is the CPU."))
|
||||
|
||||
(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."))))
|
||||
@@ -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))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user